From cb0fa34743ec8373f65b68306ecb92d4a5767439 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 4 Dec 2023 19:38:37 +0000 Subject: [PATCH] catmull-rom spline custom --- docs/entities.md | 11 +- scripts/engine.js | 7 +- scripts/entity.js | 2 - scripts/sound.js | 5 + scripts/std.js | 3 +- source/engine/2dphysics.c | 160 +- source/engine/2dphysics.h | 7 +- source/engine/3d/3dphysics.c | 47 - source/engine/3d/3dphysics.h | 4 - source/engine/3d/3pfollow.h | 3 +- source/engine/3d/model.c | 346 ++-- source/engine/3d/model.h | 19 +- source/engine/HandmadeMath.h | 116 +- source/engine/anim.c | 86 +- source/engine/anim.h | 31 +- source/engine/gameobject.c | 36 +- source/engine/gameobject.h | 13 - source/engine/input.c | 2 - source/engine/input.h | 2 - source/engine/jsffi.c | 101 +- source/engine/parson.c | 2557 ----------------------------- source/engine/parson.h | 263 --- source/engine/particle.h | 6 +- source/engine/render.c | 2 +- source/engine/sound/dsp.c | 64 +- source/engine/sprite.c | 63 +- source/engine/tinyspline.c | 2932 ---------------------------------- source/engine/tinyspline.h | 2886 --------------------------------- source/engine/transform.c | 25 +- source/engine/transform.h | 5 + source/engine/yugine.c | 4 +- 31 files changed, 583 insertions(+), 9225 deletions(-) delete mode 100644 source/engine/3d/3dphysics.c delete mode 100644 source/engine/3d/3dphysics.h delete mode 100644 source/engine/parson.c delete mode 100644 source/engine/parson.h delete mode 100644 source/engine/tinyspline.c delete mode 100644 source/engine/tinyspline.h diff --git a/docs/entities.md b/docs/entities.md index da9b1b0..3f76520 100644 --- a/docs/entities.md +++ b/docs/entities.md @@ -4,5 +4,14 @@ Entities are defined by creating a .jso script in your game directory. Variants An entity which differs from its ur will have an asterisk * next to its name. -## Ur types +Entities have components. The components all do a thing. Components are placed relative to the entity. +All entities are child to Primum, once the simulation is running. + +Entites can be static, kinematic, or dynamic. Static entities never move. Kinematic entities move via explicit command. Dynamic ones move under the auspices of the game world. + +Components, if they have a defined transform, are relative to the entity they reside on. Components cannot have components, ensuring that components can be processed and rendered in any order. + +Entities are defined via a typical scene graph system. This allows for easy reuse of components when designing levels. + +When the game is run, the movement relationships are broken, and the physics system takes over. In the physics engine, each entity is a direct child of the world. Objects can still be constrained to other objects via the physics system. diff --git a/scripts/engine.js b/scripts/engine.js index 4d165bb..b454e79 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -413,11 +413,7 @@ var Game = { { sys_cmd(1); }, - - get dt() { - return cmd(63); - }, - + wait_fns: [], wait_exec(fn) { @@ -487,4 +483,3 @@ var Asset = {}; Asset.doc = { doc: "Functions to manage the loading and unloading of assets, like sounds and images." }; - diff --git a/scripts/entity.js b/scripts/entity.js index 9266373..61cd15a 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -169,7 +169,6 @@ var gameobject = { set_gravity(x) { cmd(167, this.body, x); }, set timescale(x) { cmd(168,this.body,x); }, get timescale() { return cmd(169,this.body); }, - set phys(x) { console.warn(`Setting phys to ${x}`); set_body(1, this.body, x); }, get phys() { return q_body(0,this.body); }, get velocity() { return q_body(3, this.body); }, @@ -513,7 +512,6 @@ var gameobject = { } delete this.components; -// q_body(8,this.body); this.clear(); diff --git a/scripts/sound.js b/scripts/sound.js index 23c2ee8..6c7cb2c 100644 --- a/scripts/sound.js +++ b/scripts/sound.js @@ -76,9 +76,14 @@ var DSP = { return dsp_node.make(cmd(207,secs,decay)); }, allpass(secs, decay) { + var composite = {}; var fwd = DSP.fwd_delay(secs,-decay); var fbk = DSP.delay(secs,decay); + composite.id = fwd.id; + composite.plugin = composite.plugin.bind(fbk); + composite.unplug = dsp_node.unplug.bind(fbk); fwd.plugin(fbk); + return composite; }, lpf(f) { return dsp_node.make(cmd(186,f)); diff --git a/scripts/std.js b/scripts/std.js index e57ab81..0559231 100644 --- a/scripts/std.js +++ b/scripts/std.js @@ -296,6 +296,7 @@ Cmdline.register_cmd("cjson", function(json) { }, "Clean up a jso file."); Cmdline.register_cmd("r", function(script) { - run(script); + try { run(script); } catch(e) { STD.exit(0); } + STD.exit(0); }, "Run a script."); diff --git a/source/engine/2dphysics.c b/source/engine/2dphysics.c index a7451b9..f67c0e9 100644 --- a/source/engine/2dphysics.c +++ b/source/engine/2dphysics.c @@ -12,8 +12,6 @@ #include "2dphysics.h" -#include "tinyspline.h" - #include "jsffi.h" #include "script.h" @@ -39,6 +37,18 @@ void set_cat_mask(int cat, unsigned int mask) { category_masks[cat] = mask; } +cpTransform m3_to_cpt(HMM_Mat3 m) +{ + cpTransform t; + t.a = m.Columns[0].x; + t.c = m.Columns[0].y; + t.tx = m.Columns[0].z; + t.b = m.Columns[1].x; + t.d = m.Columns[1].y; + t.ty = m.Columns[1].z; + return t; +} + cpShape *phys2d_query_pos(cpVect pos) { cpShapeFilter filter; filter.group = CP_NO_GROUP; @@ -49,62 +59,56 @@ cpShape *phys2d_query_pos(cpVect pos) { return find; } -int *qhits; - -void querylist(cpShape *shape, cpContactPointSet *points, void *data) { - int go = shape2gameobject(shape); - int in = 0; - for (int i = 0; i < arrlen(qhits); i++) { - if (qhits[i] == go) { - in = 1; - break; - } - } - - if (!in) arrput(qhits, go); +int sort_ids(int *a, int *b) +{ + if (*a == *b) return 0; + if (*a < *b) return -1; + return 1; } -void querylistbodies(cpBody *body, void *data) { - cpBB *bbox = data; - if (cpBBContainsVect(*bbox, cpBodyGetPosition(body))) { - int go = body2id(body); - if (go < 0) return; +int *clean_ids(int *ids) +{ + qsort(ids, sizeof(*ids), arrlen(ids), sort_ids); - int in = 0; - for (int i = 0; i < arrlen(qhits); i++) { - if (qhits[i] == go) { - in = 1; - break; - } - } + int curid = -1; + for (int i = arrlen(ids)-1; i >= 0; i--) + if (ids[i] == curid) + arrdelswap(ids, i); + else + curid = ids[i]; - if (!in) arrput(qhits, go); - } + return ids; } +void querylist(cpShape *shape, cpContactPointSet *points, int *ids) { + arrput(ids,shape2gameobject(shape)); +} + +typedef struct querybox { + cpBB bb; + int *ids; +} querybox; + +void querylistbodies(cpBody *body, querybox *qb) { + if (cpBBContainsVect(qb->bb, cpBodyGetPosition(body))) + arrput(qb->ids,body2id(body)); +} + +/* Return all points from a list of points in the given boundingbox */ int *phys2d_query_box_points(HMM_Vec2 pos, HMM_Vec2 wh, HMM_Vec2 *points, int n) { - cpShape *box = cpBoxShapeNew(NULL, wh.x, wh.y, 0.f); - cpTransform T = {0}; - T.a = 1; - T.d = 1; - T.tx = pos.x; - T.ty = pos.y; - cpShapeUpdate(box, T); + cpBB bbox; + bbox = cpBBExpand(bbox, cpvadd(pos.cp, cpvmult(wh.cp,0.5))); + bbox = cpBBExpand(bbox, cpvsub(pos.cp, cpvmult(wh.cp,0.5))); + int *hits = NULL; - cpBB bbox = cpShapeGetBB(box); - - if (qhits) arrfree(qhits); - - for (int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) if (cpBBContainsVect(bbox, points[i].cp)) - arrpush(qhits, i); - } + arrpush(hits, i); - cpShapeFree(box); - - return qhits; + return hits; } +/* Return all gameobjects within the given box */ int *phys2d_query_box(HMM_Vec2 pos, HMM_Vec2 wh) { cpShape *box = cpBoxShapeNew(NULL, wh.x, wh.y, 0.f); cpTransform T = {0}; @@ -116,22 +120,24 @@ int *phys2d_query_box(HMM_Vec2 pos, HMM_Vec2 wh) { cpBB bbox = cpShapeGetBB(box); - if (qhits) arrfree(qhits); + int *ids = NULL; - cpSpaceShapeQuery(space, box, querylist, NULL); - cpSpaceEachBody(space, querylistbodies, &bbox); + querybox qb; + qb.bb = bbox; + qb.ids = ids; + + cpSpaceShapeQuery(space, box, querylist, ids); + cpSpaceEachBody(space, querylistbodies, &qb); cpShapeFree(box); - return qhits; + return clean_ids(ids); } int *phys2d_query_shape(struct phys2d_shape *shape) { - if (qhits) arrfree(qhits); - - cpSpaceShapeQuery(space, shape->shape, querylist, NULL); - - return qhits; + int *ids = NULL; + cpSpaceShapeQuery(space, shape->shape, querylist, ids); + return clean_ids(ids); } int cpshape_enabled(cpShape *c) { @@ -246,11 +252,12 @@ void phys2d_applycircle(struct phys2d_circle *circle) { struct phys2d_box *Make2DBox(int go) { struct phys2d_box *new = malloc(sizeof(struct phys2d_box)); - - new->w = 50.f; - new->h = 50.f; + new->t = (transform2d){ + .pos = {0,0}, + .angle = 0, + .scale = {0,0} + }; new->r = 0.f; - new->offset = v2zero; new->shape.go = go; new->shape.apply = phys2d_applybox; phys2d_applybox(new); @@ -261,18 +268,7 @@ struct phys2d_box *Make2DBox(int go) { } float phys2d_box_moi(struct phys2d_box *box, float m) { - return cpMomentForBox(m, box->w, box->h); -} - -cpTransform trs2cpt(HMM_Vec2 t, float r, HMM_Vec2 s) { - cpTransform T; - T.a = cos(r) * s.X; - T.b = -sin(r) * s.X; - T.c = sin(r) * s.Y; - T.d = cos(r) * s.Y; - T.tx = t.X * s.X; - T.ty = t.Y * s.Y; - return T; + return cpMomentForBox(m, box->t.scale.x, box->t.scale.y); } void phys2d_boxdel(struct phys2d_box *box) { @@ -282,10 +278,8 @@ void phys2d_boxdel(struct phys2d_box *box) { void phys2d_applybox(struct phys2d_box *box) { phys2d_boxdel(box); struct gameobject *go = id2go(box->shape.go); - cpTransform T = trs2cpt(box->offset, box->rotation, id2go(box->shape.go)->scale.XY); - float hh = box->h / 2.f; - float hw = box->w / 2.f; - cpVect verts[4] = {{-hw, -hh}, {hw, -hh}, {hw, hh}, {-hw, hh}}; + cpTransform T = m3_to_cpt(transform2d2mat(box->t)); + cpVect verts[4] = {{-0.5, -0.5}, {0.5, -0.5}, {0.5, 0.5}, {-0.5, 0.5}}; box->shape.shape = cpSpaceAddShape(space, cpPolyShapeNew(go->body, 4, verts, T, box->r)); init_phys2dshape(&box->shape, box->shape.go, box); } @@ -361,9 +355,7 @@ void phys2d_poly_setverts(struct phys2d_poly *poly, cpVect *verts) { void phys2d_applypoly(struct phys2d_poly *poly) { if (arrlen(poly->points) <= 0) return; struct gameobject *go = id2go(poly->shape.go); - - cpTransform T = trs2cpt((HMM_Vec2){0,0}, 0, go->scale.XY); - + cpTransform T = m3_to_cpt(transform2d2mat(poly->t)); cpPolyShapeSetVerts(poly->shape.shape, arrlen(poly->points), poly->points, T); cpPolyShapeSetRadius(poly->shape.shape, poly->radius); cpSpaceReindexShapesForBody(space, cpShapeGetBody(poly->shape.shape)); @@ -477,12 +469,13 @@ void phys2d_applyedge(struct phys2d_edge *edge) { struct gameobject *go = id2go(edge->shape.go); for (int i = 0; i < arrlen(edge->shapes); i++) { - HMM_Vec2 a = goscale(go, edge->points[i]); - HMM_Vec2 b = goscale(go, edge->points[i+1]); + /* Points must be scaled with gameobject, */ + HMM_Vec2 a = HMM_MulV2(go->scale.xy, edge->points[i]); + HMM_Vec2 b = HMM_MulV2(go->scale.xy, edge->points[i+1]); cpSegmentShapeSetEndpoints(edge->shapes[i], a.cp, b.cp); cpSegmentShapeSetRadius(edge->shapes[i], edge->thickness); if (i > 0 && i < arrlen(edge->shapes) - 1) - cpSegmentShapeSetNeighbors(edge->shapes[i], goscale(go,edge->points[i-1]).cp, goscale(go,edge->points[i+2]).cp); + cpSegmentShapeSetNeighbors(edge->shapes[i], HMM_MulV2(go->scale.xy,edge->points[i-1]).cp, HMM_MulV2(go->scale.xy,edge->points[i+2]).cp); go_shape_apply(NULL, edge->shapes[i], go); cpShapeSetUserData(edge->shapes[i], &edge->shape); } @@ -503,7 +496,6 @@ void phys2d_dbgdrawedge(struct phys2d_edge *edge) { HMM_Vec2 drawpoints[arrlen(edge->points)]; struct gameobject *go = id2go(edge->shape.go); - HMM_Mat3 g2w = t_go2world(go); for (int i = 0; i < arrlen(edge->points); i++) @@ -553,9 +545,7 @@ int shape_get_sensor(struct phys2d_shape *shape) { return cpShapeGetSensor(shape->shape); } -void phys2d_reindex_body(cpBody *body) { - cpSpaceReindexShapesForBody(space, body); -} +void phys2d_reindex_body(cpBody *body) { cpSpaceReindexShapesForBody(space, body); } struct postphys_cb { struct callee c; diff --git a/source/engine/2dphysics.h b/source/engine/2dphysics.h index 27d145f..5f48650 100644 --- a/source/engine/2dphysics.h +++ b/source/engine/2dphysics.h @@ -23,6 +23,7 @@ extern struct rgba sleep_color; struct phys2d_shape { cpShape *shape; + transform2d t; int go; void *data; /* The specific subtype; phys2d_circle, etc */ void (*debugdraw)(void *data); @@ -48,16 +49,14 @@ struct phys2d_segment { /* A convex polygon; defined as the convex hull around the given set of points */ struct phys2d_poly { HMM_Vec2 *points; + transform2d t; float radius; struct phys2d_shape shape; }; /* A box shape; a type of a polygon collider */ struct phys2d_box { - float w; - float h; - HMM_Vec2 offset; - float rotation; + transform2d t; /* Scale here is used as width/height */ float r; /* radius */ struct phys2d_shape shape; }; diff --git a/source/engine/3d/3dphysics.c b/source/engine/3d/3dphysics.c deleted file mode 100644 index c66dbb1..0000000 --- a/source/engine/3d/3dphysics.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "3dphysics.h" - -/* -btDefaultCollisionConfiguration *collisionConfig { -NULL}; -btCollisionDispatcher *dispatcher { -NULL}; -btBroadphaseInterface *overlappingPairCache { -NULL}; -btSequentialImpulseConstraintSolver *solver { -NULL}; -btDiscreteDynamicsWorld *dynamicsWorld { -NULL}; -btRigidBody *worldFloor { -NULL}; - -void btUpdate() -{ - // dynamicsWorld->stepSimulation(deltaT); -} - -void btInit() -{ - // collisionConfig = new btDefaultCollisionConfiguration(); - // dispatcher = new btCollisionDispatcher(collisionConfig); - // overlappingPairCache = new btDbvtBroadphase(); - // solver = new btSequentialImpulseConstraintSolver; - // dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfig); - // dynamicsWorld->setGravity(btVector3(0.f, -9.8f, 0.f)); - // btDebugDrawer = new BulletDebugDrawer_OpenGL(); - // dynamicsWorld->setDebugDrawer(btDebugDrawer); - - - //btDebugDrawer->setDebugMode(btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawAabb); - // Add camera and plane to world - // dynamicsWorld->addRigidBody(camera.body); - // camera.body->setGravity(btVector3(0.f, 0.f, 0.f)); - - // Create and add plane - // btStaticPlaneShape* floorShape = new btStaticPlaneShape(btVector3(0.f, 1.f, 0.f), 0.f); - // btDefaultMotionState* motionState = new btDefaultMotionState(); - // btVector3 bodyInertia; - // btRigidBody::btRigidBodyConstructionInfo bodyCI = btRigidBody::btRigidBodyConstructionInfo(0.f, motionState, floorShape, bodyInertia); - // worldFloor = new btRigidBody(bodyCI); - // dynamicsWorld->addRigidBody(worldFloor); -} -*/ diff --git a/source/engine/3d/3dphysics.h b/source/engine/3d/3dphysics.h deleted file mode 100644 index 3d6a394..0000000 --- a/source/engine/3d/3dphysics.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef THREEDPHYSICS_H -#define THREEDPHYSICS_H - -#endif diff --git a/source/engine/3d/3pfollow.h b/source/engine/3d/3pfollow.h index 8bfd359..ccac968 100644 --- a/source/engine/3d/3pfollow.h +++ b/source/engine/3d/3pfollow.h @@ -1,5 +1,3 @@ -// odplot productions is a trademarked name. Project Yugh is a copyrighted property. This code, however, is free to be copy and extended as you see fit. - #ifndef THIRDPERSONFOLLOW_H #define THIRDPERSONFOLLOW_H @@ -8,6 +6,7 @@ struct follow { float distance; + HMM_Vec3 offset; HMM_Quat target_rot; }; diff --git a/source/engine/3d/model.c b/source/engine/3d/model.c index 2af1d90..2865ebc 100644 --- a/source/engine/3d/model.c +++ b/source/engine/3d/model.c @@ -14,7 +14,6 @@ #include "render.h" -// #define HANDMADE_MATH_USE_TURNS #include "HandmadeMath.h" #include "math.h" @@ -100,11 +99,11 @@ struct model *GetExistingModel(const char *path) { return MakeModel(path); } -cgltf_attribute *get_attr_type(cgltf_primitive p, cgltf_attribute_type t) +cgltf_attribute *get_attr_type(cgltf_primitive *p, cgltf_attribute_type t) { - for (int i = 0; i < p.attributes_count; i++) { - if (p.attributes[i].type == t) - return &p.attributes[i]; + for (int i = 0; i < p->attributes_count; i++) { + if (p->attributes[i].type == t) + return &p->attributes[i]; } return NULL; @@ -128,7 +127,193 @@ uint32_t pack_int10_n2(float *norm) return (ni[0] & 0x3FF) | ( (ni[1] & 0x3FF) << 10) | ( (ni[2] & 0x3FF) << 20) | ( (0 & 0x3) << 30); } -struct model *MakeModel(const char *path) { +void mesh_add_material(mesh *mesh, cgltf_material *mat) +{ + if (!mat) return; + + if (mat && mat->has_pbr_metallic_roughness) { + cgltf_image *img = mat->pbr_metallic_roughness.base_color_texture.texture->image; + if (img->buffer_view) { + cgltf_buffer_view *buf = img->buffer_view; + mesh->bind.fs.images[0] = texture_fromdata(buf->buffer->data, buf->size)->id; + } else { + const char *imp = seprint("%s/%s", dirname(mesh->model->path), img->uri); + mesh->bind.fs.images[0] = texture_pullfromfile(imp)->id; + free(imp); + } + } else + // Get "no texture" tex + mesh->bind.fs.images[0] = texture_pullfromfile("k")->id; + mesh->bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){}); + + cgltf_texture *tex; + if (tex = mat->normal_texture.texture) + mesh->bind.fs.images[1] = texture_pullfromfile(tex->image->uri)->id; + else + mesh->bind.fs.images[1] = texture_pullfromfile("k")->id; +} + +void mesh_add_primitive(mesh *mesh, cgltf_primitive *prim) +{ + uint16_t *idxs; + if (prim->indices) { + int c = prim->indices->count; + idxs = malloc(sizeof(*idxs)*c); + memcpy(idxs, cgltf_buffer_view_data(prim->indices->buffer_view), sizeof(uint16_t) * c); + + mesh->bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){ + .data.ptr = idxs, + .data.size = sizeof(uint16_t) * c, + .type = SG_BUFFERTYPE_INDEXBUFFER}); + + mesh->face_count = c; + } else { + YughWarn("Model does not have indices. Generating them."); + int c = prim->attributes[0].data->count; + mesh->face_count = c; + idxs = malloc(sizeof(*idxs)*c); + + for (int z = 0; z < c; z++) + idxs[z] = z; + + mesh->bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){ + .data.ptr = idxs, + .data.size = sizeof(uint16_t) * c, + .type = SG_BUFFERTYPE_INDEXBUFFER}); + } + free(idxs); + + mesh_add_material(mesh, prim->material); + int has_norm = 0; + + for (int k = 0; k < prim->attributes_count; k++) { + cgltf_attribute attribute = prim->attributes[k]; + + int n = cgltf_accessor_unpack_floats(attribute.data, NULL, 0); /* floats per element x num elements */ + float *vs = malloc(sizeof(float)*n); + cgltf_accessor_unpack_floats(attribute.data, vs, n); + + uint32_t *packed_norms; + unsigned short *packed_coords; + + + switch (attribute.type) { + case cgltf_attribute_type_position: + + mesh->bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ + .data.ptr = vs, + .data.size = sizeof(float) * n}); + break; + + case cgltf_attribute_type_normal: + has_norm = 1; + packed_norms = malloc(mesh->face_count * sizeof(uint32_t)); + for (int i = 0; i < mesh->face_count; i++) + packed_norms[i] = pack_int10_n2(vs + i*3); + + mesh->bind.vertex_buffers[2] = sg_make_buffer(&(sg_buffer_desc){ + .data.ptr = packed_norms, + .data.size = sizeof(uint32_t) * mesh->face_count}); + + free (packed_norms); + break; + + case cgltf_attribute_type_tangent: + break; + + case cgltf_attribute_type_color: + break; + + case cgltf_attribute_type_weights: + break; + + case cgltf_attribute_type_joints: + break; + + case cgltf_attribute_type_texcoord: + packed_coords = malloc(mesh->face_count * 2 * sizeof(unsigned short)); + for (int i = 0; i < mesh->face_count*2; i++) + packed_coords[i] = pack_short_texcoord(vs[i]); + + mesh->bind.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){ + .data.ptr = packed_coords, + .data.size = sizeof(unsigned short) * 2 * mesh->face_count}); + + free(packed_coords); + break; + } + free(vs); + } + + if (!has_norm) { + uint32_t norms[mesh->face_count]; + + cgltf_attribute *pa = get_attr_type(prim, cgltf_attribute_type_position); + int n = cgltf_accessor_unpack_floats(pa->data, NULL,0); + float ps[n]; + cgltf_accessor_unpack_floats(pa->data,ps,n); + + for (int i = 0, face=0; i < mesh->face_count/3; i++, face+=9) { + int o = face; + HMM_Vec3 a = {ps[o], ps[o+1],ps[o+2]}; + o += 3; + HMM_Vec3 b = {ps[o], ps[o+1],ps[o+2]}; + o += 3; + HMM_Vec3 c = {ps[o], ps[o+1],ps[o+2]}; + HMM_Vec3 norm = HMM_NormV3(HMM_Cross(HMM_SubV3(b,a), HMM_SubV3(c,a))); + + uint32_t packed_norm = pack_int10_n2(norm.Elements); + for (int j = 0; j < 3; j++) + norms[i*3+j] = packed_norm; + } + mesh->bind.vertex_buffers[2] = sg_make_buffer(&(sg_buffer_desc){ + .data.ptr = norms, + .data.size = sizeof(uint32_t) * mesh->face_count + }); + } +} + +void model_add_cgltf_mesh(model *model, cgltf_mesh *gltf_mesh) +{ + mesh mesh = {0}; + mesh.model = model; + + for (int i = 0; i < gltf_mesh->primitives_count; i++) + mesh_add_primitive(&mesh, &gltf_mesh->primitives[i]); + + arrput(model->meshes,mesh); +} + +void model_add_cgltf_anim(model *model, cgltf_animation *anim) +{ + +} + +void model_add_cgltf_skin(model *model, cgltf_skin *skin) +{ + +} + +void model_process_node(model *model, cgltf_node *node) +{ + if (node->has_matrix) + memcpy(model->matrix.Elements, node->matrix, sizeof(float)*16); + + if (node->mesh) + model_add_cgltf_mesh(model, node->mesh); + + if (node->skin) + model_add_cgltf_skin(model, node->skin); +} + +void model_process_scene(model *model, cgltf_scene *scene) +{ + for (int i = 0; i < scene->nodes_count; i++) + model_process_node(model, scene->nodes[i]); +} + +struct model *MakeModel(const char *path) +{ YughInfo("Making the model from %s.", path); cgltf_options options = {0}; cgltf_data *data = NULL; @@ -147,150 +332,17 @@ struct model *MakeModel(const char *path) { } struct model *model = calloc(1, sizeof(*model)); - /* TODO: Optimize by grouping by material. One material per draw. */ - YughInfo("Model has %d materials.", data->materials_count); - const char *dir = dirname(path); + + model->path = path; - float vs[65535*3]; - uint16_t idxs[65535]; + if (data->scenes_count == 0 || data->scenes_count > 1) return NULL; + model_process_scene(model, data->scene); - for (int i = 0; i < data->meshes_count; i++) { - cgltf_mesh *mesh = &data->meshes[i]; - struct mesh newmesh = {0}; - arrput(model->meshes,newmesh); + for (int i = 0; i < data->meshes_count; i++) + model_add_cgltf_mesh(model, &data->meshes[i]); - YughInfo("Making mesh %d. It has %d primitives.", i, mesh->primitives_count); - - for (int j = 0; j < mesh->primitives_count; j++) { - cgltf_primitive primitive = mesh->primitives[j]; - - if (primitive.indices) { - int c = primitive.indices->count; - memcpy(idxs, cgltf_buffer_view_data(primitive.indices->buffer_view), sizeof(uint16_t) * c); - - model->meshes[j].bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){ - .data.ptr = idxs, - .data.size = sizeof(uint16_t) * c, - .type = SG_BUFFERTYPE_INDEXBUFFER}); - - model->meshes[j].face_count = c; - } else { - YughWarn("Model does not have indices. Generating them."); - int c = primitive.attributes[0].data->count; - model->meshes[j].face_count = c; - for (int z = 0; z < c; z++) - idxs[z] = z; - - model->meshes[j].bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){ - .data.ptr = idxs, - .data.size = sizeof(uint16_t) * c, - .type = SG_BUFFERTYPE_INDEXBUFFER}); - } - - struct cgltf_material *mat = primitive.material; - - if (mat && primitive.material->has_pbr_metallic_roughness) { - cgltf_image *img = primitive.material->pbr_metallic_roughness.base_color_texture.texture->image; - if (img->buffer_view) { - cgltf_buffer_view *buf = img->buffer_view; - model->meshes[j].bind.fs.images[0] = texture_fromdata(buf->buffer->data, buf->size)->id; - } else { - const char *imp = seprint("%s/%s", dir, img->uri); - model->meshes[j].bind.fs.images[0] = texture_pullfromfile(imp)->id; - free(imp); - } - } else - model->meshes[j].bind.fs.images[0] = texture_pullfromfile("k")->id; - - model->meshes[j].bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){}); - - cgltf_texture *tex; -// if (tex = primitive.material->normal_texture.texture) { -// model->meshes[j].bind.fs.images[1] = texture_pullfromfile(tex->image->uri)->id; -// }// else -// model->meshes[j].bind.fs.images[1] = texture_pullfromfile("k")->id; - - int has_norm = 0; - - for (int k = 0; k < primitive.attributes_count; k++) { - cgltf_attribute attribute = primitive.attributes[k]; - - int n = cgltf_accessor_unpack_floats(attribute.data, NULL, 0); /* floats per element x num elements */ - - cgltf_accessor_unpack_floats(attribute.data, vs, n); - - uint32_t *packed_norms; - unsigned short *packed_coords; - - switch (attribute.type) { - case cgltf_attribute_type_position: - - model->meshes[j].bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ - .data.ptr = vs, - .data.size = sizeof(float) * n}); - break; - - case cgltf_attribute_type_normal: - has_norm = 1; - packed_norms = malloc(model->meshes[j].face_count * sizeof(uint32_t));; - for (int i = 0; i < model->meshes[j].face_count; i++) - packed_norms[i] = pack_int10_n2(vs + i*3); - -// model->meshes[j].bind.vertex_buffers[2] = sg_make_buffer(&(sg_buffer_desc){ -// .data.ptr = packed_norms, -// .data.size = sizeof(uint32_t) * model->meshes[j].face_count}); - - free (packed_norms); - break; - - case cgltf_attribute_type_tangent: - break; - - case cgltf_attribute_type_texcoord: - packed_coords = malloc(model->meshes[j].face_count * 2 * sizeof(unsigned short)); - for (int i = 0; i < model->meshes[j].face_count*2; i++) - packed_coords[i] = pack_short_texcoord(vs[i]); - - model->meshes[j].bind.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){ - .data.ptr = packed_coords, - .data.size = sizeof(unsigned short) * 2 * model->meshes[j].face_count}); - - free(packed_coords); - break; - } - } - - if (!has_norm) { - YughInfo("Model does not have normals. Generating them."); - uint32_t norms[model->meshes[j].face_count]; - - - cgltf_attribute *pa = get_attr_type(primitive, cgltf_attribute_type_position); - int n = cgltf_accessor_unpack_floats(pa->data, NULL,0); - float ps[n]; - cgltf_accessor_unpack_floats(pa->data,ps,n); - - for (int i = 0, face=0; i < model->meshes[j].face_count/3; i++, face+=9) { - int o = face; - HMM_Vec3 a = {ps[o], ps[o+1],ps[o+2]}; - o += 3; - HMM_Vec3 b = {ps[o], ps[o+1],ps[o+2]}; - o += 3; - HMM_Vec3 c = {ps[o], ps[o+1],ps[o+2]}; - HMM_Vec3 norm = HMM_NormV3(HMM_Cross(HMM_SubV3(b,a), HMM_SubV3(c,a))); - - uint32_t packed_norm = pack_int10_n2(norm.Elements); - for (int j = 0; j < 3; j++) - norms[i*3+j] = packed_norm; - } - -// model->meshes[j].bind.vertex_buffers[2] = sg_make_buffer(&(sg_buffer_desc){ -// .data.ptr = norms, -// .data.size = sizeof(uint32_t) * model->meshes[j].face_count -// }); - } - } - } + for (int i = 0; i < data->animations_count; i++) + model_add_cgltf_anim(model, &data->animations[i]); shput(modelhash, path, model); diff --git a/source/engine/3d/model.h b/source/engine/3d/model.h index 78fb72e..fb9ca57 100644 --- a/source/engine/3d/model.h +++ b/source/engine/3d/model.h @@ -8,16 +8,25 @@ extern HMM_Vec3 eye; struct shader; +typedef struct material { + +} material; + +struct model; + /* A single mesh */ -struct mesh { - sg_bindings bind; +typedef struct mesh { + sg_bindings bind; /* Encapsulates material, norms, etc */ uint32_t face_count; -}; + struct model *model; +} mesh; /* A collection of meshes which create a full figure */ -struct model { +typedef struct model { struct mesh *meshes; -}; + const char *path; + HMM_Mat4 matrix; +} model; /* A model with draw information */ struct drawmodel { diff --git a/source/engine/HandmadeMath.h b/source/engine/HandmadeMath.h index 49f418b..d938e5a 100644 --- a/source/engine/HandmadeMath.h +++ b/source/engine/HandmadeMath.h @@ -228,6 +228,8 @@ typedef union HMM_Vec3 { float X, Y, Z; }; + struct { float x, y, z; }; + struct { float U, V, W; @@ -244,6 +246,13 @@ typedef union HMM_Vec3 { float _Ignored0; }; + struct + { + HMM_Vec2 xy; + float _Ignored4; + }; + + struct { float _Ignored1; @@ -268,6 +277,28 @@ typedef union HMM_Vec3 { static const HMM_Vec3 v3zero = {0,0,0}; +typedef union HMM_Quat { + struct + { + union { + HMM_Vec3 XYZ; + struct + { + float X, Y, Z; + }; + }; + + float W; + }; + + float Elements[4]; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSE; +#endif +} HMM_Quat; + + typedef union HMM_Vec4 { struct { @@ -277,6 +308,7 @@ typedef union HMM_Vec4 { { float X, Y, Z; }; + HMM_Vec3 xyz; }; float W; @@ -301,6 +333,23 @@ typedef union HMM_Vec4 { float _Ignored1; }; + struct { + HMM_Vec2 xy; + float _ig0; + float _ig1; + }; + + struct { + HMM_Vec2 _2; + float _ig2; + float _ig3; + }; + + struct { + HMM_Vec3 _3; + float _ig4; + }; + struct { float _Ignored2; @@ -315,6 +364,8 @@ typedef union HMM_Vec4 { HMM_Vec2 ZW; }; + HMM_Quat quat; + float Elements[4]; #ifdef HANDMADE_MATH__USE_SSE @@ -341,26 +392,6 @@ typedef union HMM_Mat4 { } HMM_Mat4; -typedef union HMM_Quat { - struct - { - union { - HMM_Vec3 XYZ; - struct - { - float X, Y, Z; - }; - }; - - float W; - }; - - float Elements[4]; - -#ifdef HANDMADE_MATH__USE_SSE - __m128 SSE; -#endif -} HMM_Quat; typedef signed int HMM_Bool; @@ -552,6 +583,7 @@ static inline HMM_Vec2 HMM_AddV2(HMM_Vec2 Left, HMM_Vec2 Right) { return Result; } + static inline HMM_Vec3 HMM_AddV3(HMM_Vec3 Left, HMM_Vec3 Right) { HMM_Vec3 Result; @@ -776,6 +808,8 @@ static inline float HMM_DotV2(HMM_Vec2 Left, HMM_Vec2 Right) { return (Left.X * Right.X) + (Left.Y * Right.Y); } + + static inline HMM_Vec2 HMM_ProjV2(HMM_Vec2 a, HMM_Vec2 b) { return HMM_MulV2F(b, HMM_DotV2(a,b)/HMM_DotV2(b,b)); @@ -836,6 +870,11 @@ static inline float HMM_LenV2(HMM_Vec2 A) { return HMM_SqrtF(HMM_LenSqrV2(A)); } +static inline float HMM_AngleV2(HMM_Vec2 a, HMM_Vec2 b) +{ + return acos(HMM_DotV2(a,b)/(HMM_LenV2(a)*HMM_LenV2(b))); +} + static inline float HMM_DistV2(HMM_Vec2 a, HMM_Vec2 b) { return HMM_LenV2(HMM_SubV2(a,b)); } @@ -844,10 +883,20 @@ static inline float HMM_LenV3(HMM_Vec3 A) { return HMM_SqrtF(HMM_LenSqrV3(A)); } +static inline float HMM_AngleV3(HMM_Vec3 a, HMM_Vec3 b) +{ + return acos(HMM_DotV3(a,b)/(HMM_LenV3(a)*HMM_LenV3(b))); +} + static inline float HMM_LenV4(HMM_Vec4 A) { return HMM_SqrtF(HMM_LenSqrV4(A)); } +static inline float HMM_AngleV4(HMM_Vec4 a, HMM_Vec4 b) +{ + return acos(HMM_DotV4(a,b)/(HMM_LenV4(a)*HMM_LenV4(b))); +} + static inline HMM_Vec2 HMM_NormV2(HMM_Vec2 A) { // HMM_MulV2F(A, 1.0/HMM_LenV2(A)+FLOAT_MIN); return HMM_MulV2F(A, HMM_InvSqrtF(HMM_DotV2(A, A))); @@ -953,6 +1002,7 @@ static inline HMM_Mat2 HMM_RotateM2(float angle) return result; } + static inline HMM_Mat2 HMM_AddM2(HMM_Mat2 Left, HMM_Mat2 Right) { HMM_Mat2 Result; @@ -1058,6 +1108,29 @@ static inline HMM_Mat3 HMM_M3D(float Diagonal) { return Result; } +static inline HMM_Mat3 HMM_Translate2D(HMM_Vec2 p) +{ + HMM_Mat3 res = HMM_M3D(1); + res.Columns[2].XY = p; + return res; +} + +static inline HMM_Mat3 HMM_RotateM3(float angle) +{ + HMM_Mat3 r = HMM_M3D(1); + r.Columns[0] = (HMM_Vec3){cos(angle), sin(angle), 0}; + r.Columns[1] = (HMM_Vec3){-sin(angle), cos(angle), 0}; + return r; +} + +static inline HMM_Mat3 HMM_ScaleM3(HMM_Vec2 s) +{ + HMM_Mat3 sm = HMM_M3D(1); + sm.Columns[0].X = s.x; + sm.Columns[1].Y = s.y; + return sm; +} + static inline HMM_Mat3 HMM_TransposeM3(HMM_Mat3 Matrix) { HMM_Mat3 Result = Matrix; @@ -1200,12 +1273,11 @@ static inline float HMM_DeterminantM3(HMM_Mat3 Matrix) { return HMM_DotV3(Cross.Columns[2], Matrix.Columns[2]); } -static inline HMM_Mat3 HMM_M2BasisPos(HMM_Mat2 basis, HMM_Vec2 pos) +static inline HMM_Mat3 HMM_M2Basis(HMM_Mat2 basis) { HMM_Mat3 m; m.Columns[0].XY = basis.Columns[0]; m.Columns[1].XY = basis.Columns[1]; - m.Columns[2].XY = pos; m.Columns[2].Z = 1; return m; } diff --git a/source/engine/anim.c b/source/engine/anim.c index 9898356..66db0bc 100644 --- a/source/engine/anim.c +++ b/source/engine/anim.c @@ -2,59 +2,53 @@ #include "log.h" #include "stb_ds.h" -struct anim make_anim() { - struct anim a = {0}; - a.interp = 1; - - return a; +HMM_Vec4 sample_linear(sampler *sampler, float time, int prev, int next) +{ + if (sampler->rotation) + return (HMM_Vec4)HMM_SLerp(sampler->data[prev].quat, time, sampler->data[next].quat); + else + return HMM_LerpV4(sampler->data[prev], time, sampler->data[next]); } -void free_anim(struct anim a) { - arrfree(a.frames); +HMM_Vec4 sample_cubicspline(sampler *sampler, float t, int prev, int next) +{ + float t2 = t*t; + float t3 = t2*t; + float td = sampler->times[next]-sampler->times[prev]; + + HMM_Vec4 v = HMM_MulV4F(sampler->data[prev*3+1], (2*t3-3*t2+1)); + v = HMM_AddV4(v, HMM_MulV4F(sampler->data[prev*3+2], td*(t3-2*t2+t))); + v = HMM_AddV4(v, HMM_MulV4F(sampler->data[next*3+1], 3*t2-2*t3)); + v = HMM_AddV4(v, HMM_MulV4F(sampler->data[next*3], td*(t3-t2))); + return v; } -struct anim anim_add_keyframe(struct anim a, struct keyframe key) { - arrput(a.frames, key); +HMM_Vec4 sample_sampler(sampler *sampler, float time) +{ + int previous_time=0; + int next_time=0; - return a; -} - -double interval(struct keyframe a, struct keyframe b, double t) { - return (t - a.time) / (b.time - a.time); -} - -double near_val(struct anim anim, double t) { - for (int i = 0; i < arrlen(anim.frames) - 1; i++) { - - if (t > anim.frames[i + 1].time) - continue; - - return (interval(anim.frames[i], anim.frames[i + 1], t) >= 0.5f ? anim.frames[i + 1].val : anim.frames[i].val); + for (int i = 1; i < arrlen(sampler->times); i++) { + if (time < sampler->times[i]) { + previous_time = sampler->times[i-1]; + next_time = sampler->times[i]; + break; + } } - return arrlast(anim.frames).val; -} + float td = sampler->times[next_time]-sampler->times[previous_time]; + float t = (time - sampler->times[previous_time])/td; -double lerp_val(struct anim anim, double t) { - - for (int i = 0; i < arrlen(anim.frames) - 1; i++) { - if (t > anim.frames[i + 1].time) - continue; - - double intv = interval(anim.frames[i], anim.frames[i + 1], t); - return ((1 - intv) * anim.frames[i].val) + (intv * anim.frames[i + 1].val); + + switch(sampler->type) { + case LINEAR: + return sample_linear(sampler,t,previous_time,next_time); + break; + case STEP: + return sampler->data[previous_time]; + break; + case CUBICSPLINE: + return sample_cubicspline(sampler,t, previous_time, next_time); + break; } - - return arrlast(anim.frames).val; -} - -double cubic_val(struct anim anim, double t) { - return 0.0f; -} - -double anim_val(struct anim anim, double t) { - if (anim.interp == 0) - return near_val(anim, t); - - return lerp_val(anim, t); } diff --git a/source/engine/anim.h b/source/engine/anim.h index 91ce0dd..274b578 100644 --- a/source/engine/anim.h +++ b/source/engine/anim.h @@ -1,19 +1,34 @@ #ifndef ANIM_H #define ANIM_H +#include "HandmadeMath.h" + struct keyframe { double time; double val; }; -struct anim { - struct keyframe *frames; - int loop; - int interp; +#define LINEAR 0 +#define STEP 1 +#define CUBICSPLINE 2 + +typedef struct sampler { + float *times; + HMM_Vec4 *data; + int type; + int rotation; +} sampler; + +struct anim_channel { + sampler *sampler; }; -struct anim make_anim(); -struct anim anim_add_keyframe(struct anim a, struct keyframe f); -double anim_val(struct anim anim, double t); +struct animation { + char *name; + double time; + struct anim_channel *channels; +}; -#endif \ No newline at end of file +HMM_Vec4 sample_sampler(sampler *sampler, float time); + +#endif diff --git a/source/engine/gameobject.c b/source/engine/gameobject.c index d7a0af6..1f02db0 100644 --- a/source/engine/gameobject.c +++ b/source/engine/gameobject.c @@ -1,13 +1,7 @@ #include "gameobject.h" #include "2dphysics.h" -#include "debugdraw.h" -#include "input.h" #include "log.h" -#include "resources.h" -#include "script.h" -#include "shader.h" -#include "sprite.h" #include #include #include "debugdraw.h" @@ -82,8 +76,6 @@ HMM_Vec2 go2world(struct gameobject *go, HMM_Vec2 pos) { return mat_t_pos(t_go2w HMM_Vec2 world2go(struct gameobject *go, HMM_Vec2 pos) { return mat_t_pos(t_world2go(go), pos); } -HMM_Vec2 goscale(struct gameobject *go, HMM_Vec2 pos) { return HMM_MulV2(go->scale.XY, pos); } - HMM_Mat3 t_go2world(struct gameobject *go) { return transform2d2mat(go2t(go)); } HMM_Mat3 t_world2go(struct gameobject *go) { return HMM_InvGeneralM3(t_go2world(go)); } @@ -94,9 +86,8 @@ HMM_Mat4 t3d_world2go(struct gameobject *go) { return HMM_InvGeneralM4(t3d_go2wo int pos2gameobject(HMM_Vec2 pos) { cpShape *hit = phys2d_query_pos(pos.cp); - if (hit) { + if (hit) return shape2gameobject(hit); - } for (int i = 0; i < arrlen(gameobjects); i++) { if (!gameobjects[i].body) continue; @@ -290,23 +281,6 @@ void gameobjects_cleanup() { arrsetlen(go_toclean, 0); } -void gameobject_move(struct gameobject *go, HMM_Vec2 vec) { - cpVect p = cpBodyGetPosition(go->body); - p.x += vec.x; - p.y += vec.y; - cpBodySetPosition(go->body, p); - - phys2d_reindex_body(go->body); -} - -void gameobject_rotate(struct gameobject *go, float as) { - cpFloat a = cpBodyGetAngle(go->body); - a += as * deltaT; - cpBodySetAngle(go->body, a); - - phys2d_reindex_body(go->body); -} - void gameobject_setangle(struct gameobject *go, float angle) { cpBodySetAngle(go->body, angle); phys2d_reindex_body(go->body); @@ -315,7 +289,6 @@ void gameobject_setangle(struct gameobject *go, float angle) { void gameobject_setpos(struct gameobject *go, cpVect vec) { if (!go || !go->body) return; cpBodySetPosition(go->body, vec); - phys2d_reindex_body(go->body); } @@ -329,13 +302,6 @@ void gameobject_draw_debug(int go) { if (!g || !g->body) return; cpVect pos = cpBodyGetPosition(g->body); - struct rgba color = { - .r = 0.76*255, - .b = 0.38*255, - .g = 255, - .a = 255 - }; - cpBodyEachShape(g->body, body_draw_shapes_dbg, NULL); } diff --git a/source/engine/gameobject.h b/source/engine/gameobject.h index ae5ef94..e446dc7 100644 --- a/source/engine/gameobject.h +++ b/source/engine/gameobject.h @@ -2,10 +2,7 @@ #define GAMEOBJECT_H #include "2dphysics.h" -#include "config.h" #include -#include -#include #include "quickjs/quickjs.h" #include "HandmadeMath.h" #include "transform.h" @@ -29,10 +26,6 @@ dag_rm(p->parent,p);\ }while(0) -struct shader; -struct sprite; -struct component; - typedef struct gameobject { cpBodyType bodytype; cpBody *body; /* NULL if this object is dead; has 2d position and rotation, relative to global 0 */ @@ -49,7 +42,6 @@ typedef struct gameobject { float damping; unsigned int layer; cpShapeFilter filter; - int id; struct phys_cbs cbs; struct shape_cb *shape_cbs; JSValue ref; @@ -89,11 +81,6 @@ float go_worldangle(struct gameobject *go); float go2angle(struct gameobject *go); -HMM_Vec2 goscale(struct gameobject *go, HMM_Vec2 pos); -HMM_Vec2 gotpos(struct gameobject *go, HMM_Vec2 pos); - -HMM_Mat3 mt_rst(transform2d t); - struct gameobject *id2go(int id); int go2id(struct gameobject *go); int body2id(cpBody *body); diff --git a/source/engine/input.c b/source/engine/input.c index 453a40a..549865d 100644 --- a/source/engine/input.c +++ b/source/engine/input.c @@ -11,8 +11,6 @@ #include #include "resources.h" -float deltaT = 0; - static int mouse_states[3] = {INPUT_UP}; static int key_states[512] = {INPUT_UP}; diff --git a/source/engine/input.h b/source/engine/input.h index 9657675..f46410e 100644 --- a/source/engine/input.h +++ b/source/engine/input.h @@ -16,8 +16,6 @@ extern HMM_Vec2 mouse_delta; #define INPUT_UP 1 #define INPUT_REPEAT 2 -extern float deltaT; - void input_init(); void input_poll(double wait); diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index 0d6e460..fbb9a5f 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -18,8 +18,8 @@ #include "sprite.h" #include "stb_ds.h" #include "string.h" -#include "tinyspline.h" #include "window.h" +#include "spline.h" #include "yugine.h" #include #include "resources.h" @@ -53,8 +53,8 @@ static JSValue TYPE##2js(TYPE *n) { \ QJSCLASS(dsp_node) -// gamobject2js, js2gameobject deals with gameobject* -// go2js,js2go deals with gameobject ids +// gameobject2js, js2gameobject deals with gameobject*, converted to ids +// go2js,js2go deals with gameobject* QJSCLASS(gameobject) @@ -97,7 +97,6 @@ JSValue gos2ref(int *go) return array; } - JSValue js_getpropstr(JSValue v, const char *str) { JSValue p = JS_GetPropertyStr(js,v,str); @@ -399,42 +398,23 @@ JSValue bb2js(struct boundingbox bb) } JSValue duk_spline_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { -// static_assert(sizeof(tsReal) * 2 == sizeof(HMM_Vec2)); - - tsBSpline spline; - - int degrees = js2int(argv[1]); - int d = js2int(argv[2]); /* dimensions */ + int degrees = js2int(argv[1]); /* not used */ + int d = js2int(argv[2]); /* dimensions: 1d, 2d, 3d ...*/ + /* + 0: hermite-cubic + 1: catmull-rom + 2: b-spline + 3: bezier + */ int type = js2int(argv[3]); HMM_Vec2 *points = js2cpvec2arr(argv[4]); size_t nsamples = js2int(argv[5]); - - tsStatus status; - ts_bspline_new(arrlen(points), d, degrees, type, &spline, &status); - - if (status.code) - YughCritical("Spline creation error %d: %s", status.code, status.message); - - ts_bspline_set_control_points(&spline, (tsReal*)points, &status); - - if (status.code) - YughCritical("Spline creation error %d: %s", status.code, status.message); - - HMM_Vec2 *samples = malloc(nsamples*sizeof(HMM_Vec2)); - - size_t rsamples; - /* TODO: This does not work with Clang/GCC due to UB */ - ts_bspline_sample(&spline, nsamples, (tsReal **)&samples, &rsamples, &status); - - if (status.code) - YughCritical("Spline creation error %d: %s", status.code, status.message); + HMM_Vec2 *samples = catmull_rom_ma_v2(points, nsamples); JSValue arr = vecarr2js(samples, nsamples); - - ts_bspline_free(&spline); free(samples); - return arr; + return JS_UNDEFINED; } JSValue ints2js(int *ints) { @@ -523,6 +503,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) const char *str2 = NULL; const void *d1 = NULL; const void *d2 = NULL; + int *ids = NULL; JSValue ret = JS_UNDEFINED; switch (cmd) { @@ -754,7 +735,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 52: - ret = gos2ref(phys2d_query_box(js2vec2(argv[1]), js2vec2(argv[2]))); + ids = phys2d_query_box(js2vec2(argv[1]), js2vec2(argv[2])); + ret = gos2ref(ids); + arrfree(ids); break; case 53: @@ -799,7 +782,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 63: - ret = JS_NewFloat64(js, deltaT); break; case 64: @@ -867,7 +849,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 80: - ret = gos2ref(phys2d_query_shape(js2ptr(argv[1]))); + ids = phys2d_query_shape(js2ptr(argv[1])); + ret = gos2ref(ids); + arrfree(ids); break; case 81: @@ -891,7 +875,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 86: - ret = gos2ref(phys2d_query_box_points(js2vec2(argv[1]), js2vec2(argv[2]), js2cpvec2arr(argv[3]), js2int(argv[4]))); + ids = phys2d_query_box_points(js2vec2(argv[1]), js2vec2(argv[2]), js2cpvec2arr(argv[3]), js2int(argv[4])); + ret = gos2ref(ids); + arrfree(ids); break; case 87: @@ -1150,6 +1136,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 147: + YughWarn("EXITING"); exit(js2int(argv[1])); break; case 148: @@ -1549,7 +1536,6 @@ JSValue duk_set_body(JSContext *js, JSValueConst this, int argc, JSValueConst *a break; case 3: - gameobject_move(go, js2vec2(argv[2])); break; case 4: @@ -1557,11 +1543,9 @@ JSValue duk_set_body(JSContext *js, JSValueConst this, int argc, JSValueConst *a return JS_UNDEFINED; case 5: -// go->flipx = JS_ToBool(js, argv[2]); break; case 6: -// go->flipy = JS_ToBool(js, argv[2]); break; case 7: @@ -1644,7 +1628,7 @@ JSValue duk_q_body(JSContext *js, JSValueConst this, int argc, JSValueConst *arg JSValue duk_make_sprite(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { JSValue sprite = JS_NewObject(js); - js_setprop_str(sprite,"id",JS_NewInt64(js, make_sprite(jsgo2id(argv[0])))); + js_setprop_str(sprite,"id",JS_NewInt64(js, make_sprite(js2gameobject(argv[0])))); return sprite; } @@ -1666,9 +1650,8 @@ JSValue duk_make_box2d(JSContext *js, JSValueConst this, int argc, JSValueConst HMM_Vec2 size = js2vec2(argv[1]); struct phys2d_box *box = Make2DBox(go); - box->w = size.x; - box->h = size.y; - box->offset = js2vec2(argv[2]); + box->t.scale = js2vec2(argv[1]); + box->t.pos = js2vec2(argv[2]); phys2d_applybox(box); @@ -1687,17 +1670,15 @@ JSValue duk_cmd_box2d(JSContext *js, JSValueConst this, int argc, JSValueConst * switch (cmd) { case 0: - arg = js2vec2(argv[2]); - box->w = arg.x; - box->h = arg.y; + box->t.scale = js2vec2(argv[2]); break; case 1: - box->offset = js2vec2(argv[2]); + box->t.pos = js2vec2(argv[2]); break; case 2: - box->rotation = js2number(argv[2]); + box->t.angle = js2number(argv[2]); break; } @@ -1830,28 +1811,6 @@ JSValue duk_inflate_cpv(JSContext *js, JSValueConst this, int argc, JSValueConst /* These are anims for controlling properties on an object */ JSValue duk_anim(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { - JSValue prop = argv[0]; - int keyframes = js_arrlen(argv[1]); - YughInfo("Processing %d keyframes.", keyframes); - - struct anim a = make_anim(); - - for (int i = 0; i < keyframes; i++) { - struct keyframe k; - HMM_Vec2 v = js2vec2(js_getpropidx( argv[1], i)); - k.time = v.y; - k.val = v.x; - a = anim_add_keyframe(a, k); - } - - for (double i = 0; i < 3.0; i = i + 0.1) { - YughInfo("Val is now %f at time %f", anim_val(a, i), i); - JSValue vv = num2js(anim_val(a, i)); - JSValue e = JS_Call(js, prop, globalThis, 1, &vv); - JS_FreeValue(js,e); - JS_FreeValue(js,vv); - } - return JS_UNDEFINED; } diff --git a/source/engine/parson.c b/source/engine/parson.c deleted file mode 100644 index a2ef266..0000000 --- a/source/engine/parson.c +++ /dev/null @@ -1,2557 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Parson 1.4.0 (https://github.com/kgabis/parson) - Copyright (c) 2012 - 2022 Krzysztof Gabis - - 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. -*/ -#ifdef _MSC_VER -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif /* _CRT_SECURE_NO_WARNINGS */ -#endif /* _MSC_VER */ - -#include "parson.h" - -#define PARSON_IMPL_VERSION_MAJOR 1 -#define PARSON_IMPL_VERSION_MINOR 4 -#define PARSON_IMPL_VERSION_PATCH 0 - -#if (PARSON_VERSION_MAJOR != PARSON_IMPL_VERSION_MAJOR) || (PARSON_VERSION_MINOR != PARSON_IMPL_VERSION_MINOR) || (PARSON_VERSION_PATCH != PARSON_IMPL_VERSION_PATCH) -#error "parson version mismatch between parson.c and parson.h" -#endif - -#include -#include -#include -#include -#include -#include - -/* Apparently sscanf is not implemented in some "standard" libraries, so don't use it, if you - * don't have to. */ -#ifdef sscanf -#undef sscanf -#define sscanf THINK_TWICE_ABOUT_USING_SSCANF -#endif - -/* strcpy is unsafe */ -#ifdef strcpy -#undef strcpy -#endif -#define strcpy USE_MEMCPY_INSTEAD_OF_STRCPY - -#define STARTING_CAPACITY 16 -#define MAX_NESTING 2048 - -#ifndef PARSON_DEFAULT_FLOAT_FORMAT -#define PARSON_DEFAULT_FLOAT_FORMAT "%1.17g" /* do not increase precision without incresing NUM_BUF_SIZE */ -#endif - -#ifndef PARSON_NUM_BUF_SIZE -#define PARSON_NUM_BUF_SIZE 64 /* double printed with "%1.17g" shouldn't be longer than 25 bytes so let's be paranoid and use 64 */ -#endif - -#define SIZEOF_TOKEN(a) (sizeof(a) - 1) -#define SKIP_CHAR(str) ((*str)++) -#define SKIP_WHITESPACES(str) \ - while (isspace((unsigned char)(**str))) { \ - SKIP_CHAR(str); \ - } -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -#undef malloc -#undef free - -#if defined(isnan) && defined(isinf) -#define IS_NUMBER_INVALID(x) (isnan((x)) || isinf((x))) -#else -#define IS_NUMBER_INVALID(x) (((x)*0.0) != 0.0) -#endif - -#define OBJECT_INVALID_IX ((size_t)-1) - -static JSON_Malloc_Function parson_malloc = malloc; -static JSON_Free_Function parson_free = free; - -static int parson_escape_slashes = 1; - -static char *parson_float_format = NULL; - -#define IS_CONT(b) (((unsigned char)(b)&0xC0) == 0x80) /* is utf-8 continuation byte */ - -typedef int parson_bool_t; - -#define PARSON_TRUE 1 -#define PARSON_FALSE 0 - -typedef struct json_string { - char *chars; - size_t length; -} JSON_String; - -/* Type definitions */ -typedef union json_value_value { - JSON_String string; - double number; - JSON_Object *object; - JSON_Array *array; - int boolean; - int null; -} JSON_Value_Value; - -struct json_value_t { - JSON_Value *parent; - JSON_Value_Type type; - JSON_Value_Value value; -}; - -struct json_object_t { - JSON_Value *wrapping_value; - size_t *cells; - unsigned long *hashes; - char **names; - JSON_Value **values; - size_t *cell_ixs; - size_t count; - size_t item_capacity; - size_t cell_capacity; -}; - -struct json_array_t { - JSON_Value *wrapping_value; - JSON_Value **items; - size_t count; - size_t capacity; -}; - -/* Various */ -static char *read_file(const char *filename); -static void remove_comments(char *string, const char *start_token, const char *end_token); -static char *parson_strndup(const char *string, size_t n); -static char *parson_strdup(const char *string); -static int hex_char_to_int(char c); -static JSON_Status parse_utf16_hex(const char *string, unsigned int *result); -static int num_bytes_in_utf8_sequence(unsigned char c); -static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len); -static parson_bool_t is_valid_utf8(const char *string, size_t string_len); -static parson_bool_t is_decimal(const char *string, size_t length); -static unsigned long hash_string(const char *string, size_t n); - -/* JSON Object */ -static JSON_Object *json_object_make(JSON_Value *wrapping_value); -static JSON_Status json_object_init(JSON_Object *object, size_t capacity); -static void json_object_deinit(JSON_Object *object, parson_bool_t free_keys, parson_bool_t free_values); -static JSON_Status json_object_grow_and_rehash(JSON_Object *object); -static size_t json_object_get_cell_ix(const JSON_Object *object, const char *key, size_t key_len, unsigned long hash, parson_bool_t *out_found); -static JSON_Status json_object_add(JSON_Object *object, char *name, JSON_Value *value); -static JSON_Value *json_object_getn_value(const JSON_Object *object, const char *name, size_t name_len); -static JSON_Status json_object_remove_internal(JSON_Object *object, const char *name, parson_bool_t free_value); -static JSON_Status json_object_dotremove_internal(JSON_Object *object, const char *name, parson_bool_t free_value); -static void json_object_free(JSON_Object *object); - -/* JSON Array */ -static JSON_Array *json_array_make(JSON_Value *wrapping_value); -static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value); -static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity); -static void json_array_free(JSON_Array *array); - -/* JSON Value */ -static JSON_Value *json_value_init_string_no_copy(char *string, size_t length); -static const JSON_String *json_value_get_string_desc(const JSON_Value *value); - -/* Parser */ -static JSON_Status skip_quotes(const char **string); -static JSON_Status parse_utf16(const char **unprocessed, char **processed); -static char *process_string(const char *input, size_t input_len, size_t *output_len); -static char *get_quoted_string(const char **string, size_t *output_string_len); -static JSON_Value *parse_object_value(const char **string, size_t nesting); -static JSON_Value *parse_array_value(const char **string, size_t nesting); -static JSON_Value *parse_string_value(const char **string); -static JSON_Value *parse_boolean_value(const char **string); -static JSON_Value *parse_number_value(const char **string); -static JSON_Value *parse_null_value(const char **string); -static JSON_Value *parse_value(const char **string, size_t nesting); - -/* Serialization */ -static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf); -static int json_serialize_string(const char *string, size_t len, char *buf); -static int append_indent(char *buf, int level); -static int append_string(char *buf, const char *string); - -/* Various */ -static char *read_file(const char *filename) { - FILE *fp = fopen(filename, "r"); - size_t size_to_read = 0; - size_t size_read = 0; - long pos; - char *file_contents; - if (!fp) { - return NULL; - } - fseek(fp, 0L, SEEK_END); - pos = ftell(fp); - if (pos < 0) { - fclose(fp); - return NULL; - } - size_to_read = pos; - rewind(fp); - file_contents = (char *)parson_malloc(sizeof(char) * (size_to_read + 1)); - if (!file_contents) { - fclose(fp); - return NULL; - } - size_read = fread(file_contents, 1, size_to_read, fp); - if (size_read == 0 || ferror(fp)) { - fclose(fp); - parson_free(file_contents); - return NULL; - } - fclose(fp); - file_contents[size_read] = '\0'; - return file_contents; -} - -static void remove_comments(char *string, const char *start_token, const char *end_token) { - parson_bool_t in_string = PARSON_FALSE, escaped = PARSON_FALSE; - size_t i; - char *ptr = NULL, current_char; - size_t start_token_len = strlen(start_token); - size_t end_token_len = strlen(end_token); - if (start_token_len == 0 || end_token_len == 0) { - return; - } - while ((current_char = *string) != '\0') { - if (current_char == '\\' && !escaped) { - escaped = PARSON_TRUE; - string++; - continue; - } else if (current_char == '\"' && !escaped) { - in_string = !in_string; - } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) { - for (i = 0; i < start_token_len; i++) { - string[i] = ' '; - } - string = string + start_token_len; - ptr = strstr(string, end_token); - if (!ptr) { - return; - } - for (i = 0; i < (ptr - string) + end_token_len; i++) { - string[i] = ' '; - } - string = ptr + end_token_len - 1; - } - escaped = PARSON_FALSE; - string++; - } -} - -static char *parson_strndup(const char *string, size_t n) { - /* We expect the caller has validated that 'n' fits within the input buffer. */ - char *output_string = (char *)parson_malloc(n + 1); - if (!output_string) { - return NULL; - } - output_string[n] = '\0'; - memcpy(output_string, string, n); - return output_string; -} - -static char *parson_strdup(const char *string) { - return parson_strndup(string, strlen(string)); -} - -static int hex_char_to_int(char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } else if (c >= 'a' && c <= 'f') { - return c - 'a' + 10; - } else if (c >= 'A' && c <= 'F') { - return c - 'A' + 10; - } - return -1; -} - -static JSON_Status parse_utf16_hex(const char *s, unsigned int *result) { - int x1, x2, x3, x4; - if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0' || s[3] == '\0') { - return JSONFailure; - } - x1 = hex_char_to_int(s[0]); - x2 = hex_char_to_int(s[1]); - x3 = hex_char_to_int(s[2]); - x4 = hex_char_to_int(s[3]); - if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) { - return JSONFailure; - } - *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4); - return JSONSuccess; -} - -static int num_bytes_in_utf8_sequence(unsigned char c) { - if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) { - return 0; - } else if ((c & 0x80) == 0) { /* 0xxxxxxx */ - return 1; - } else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */ - return 2; - } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */ - return 3; - } else if ((c & 0xF8) == 0xF0) { /* 11110xxx */ - return 4; - } - return 0; /* won't happen */ -} - -static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len) { - unsigned int cp = 0; - *len = num_bytes_in_utf8_sequence(string[0]); - - if (*len == 1) { - cp = string[0]; - } else if (*len == 2 && IS_CONT(string[1])) { - cp = string[0] & 0x1F; - cp = (cp << 6) | (string[1] & 0x3F); - } else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) { - cp = ((unsigned char)string[0]) & 0xF; - cp = (cp << 6) | (string[1] & 0x3F); - cp = (cp << 6) | (string[2] & 0x3F); - } else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) { - cp = string[0] & 0x7; - cp = (cp << 6) | (string[1] & 0x3F); - cp = (cp << 6) | (string[2] & 0x3F); - cp = (cp << 6) | (string[3] & 0x3F); - } else { - return JSONFailure; - } - - /* overlong encodings */ - if ((cp < 0x80 && *len > 1) || - (cp < 0x800 && *len > 2) || - (cp < 0x10000 && *len > 3)) { - return JSONFailure; - } - - /* invalid unicode */ - if (cp > 0x10FFFF) { - return JSONFailure; - } - - /* surrogate halves */ - if (cp >= 0xD800 && cp <= 0xDFFF) { - return JSONFailure; - } - - return JSONSuccess; -} - -static int is_valid_utf8(const char *string, size_t string_len) { - int len = 0; - const char *string_end = string + string_len; - while (string < string_end) { - if (verify_utf8_sequence((const unsigned char *)string, &len) != JSONSuccess) { - return PARSON_FALSE; - } - string += len; - } - return PARSON_TRUE; -} - -static parson_bool_t is_decimal(const char *string, size_t length) { - if (length > 1 && string[0] == '0' && string[1] != '.') { - return PARSON_FALSE; - } - if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { - return PARSON_FALSE; - } - while (length--) { - if (strchr("xX", string[length])) { - return PARSON_FALSE; - } - } - return PARSON_TRUE; -} - -static unsigned long hash_string(const char *string, size_t n) { -#ifdef PARSON_FORCE_HASH_COLLISIONS - (void)string; - (void)n; - return 0; -#else - unsigned long hash = 5381; - unsigned char c; - size_t i = 0; - for (i = 0; i < n; i++) { - c = string[i]; - if (c == '\0') { - break; - } - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - return hash; -#endif -} - -/* JSON Object */ -static JSON_Object *json_object_make(JSON_Value *wrapping_value) { - JSON_Status res = JSONFailure; - JSON_Object *new_obj = (JSON_Object *)parson_malloc(sizeof(JSON_Object)); - if (new_obj == NULL) { - return NULL; - } - new_obj->wrapping_value = wrapping_value; - res = json_object_init(new_obj, 0); - if (res != JSONSuccess) { - parson_free(new_obj); - return NULL; - } - return new_obj; -} - -static JSON_Status json_object_init(JSON_Object *object, size_t capacity) { - unsigned int i = 0; - - object->cells = NULL; - object->names = NULL; - object->values = NULL; - object->cell_ixs = NULL; - object->hashes = NULL; - - object->count = 0; - object->cell_capacity = capacity; - object->item_capacity = (unsigned int)(capacity * 0.7f); - - if (capacity == 0) { - return JSONSuccess; - } - - object->cells = (size_t *)parson_malloc(object->cell_capacity * sizeof(*object->cells)); - object->names = (char **)parson_malloc(object->item_capacity * sizeof(*object->names)); - object->values = (JSON_Value **)parson_malloc(object->item_capacity * sizeof(*object->values)); - object->cell_ixs = (size_t *)parson_malloc(object->item_capacity * sizeof(*object->cell_ixs)); - object->hashes = (unsigned long *)parson_malloc(object->item_capacity * sizeof(*object->hashes)); - if (object->cells == NULL || object->names == NULL || object->values == NULL || object->cell_ixs == NULL || object->hashes == NULL) { - goto error; - } - for (i = 0; i < object->cell_capacity; i++) { - object->cells[i] = OBJECT_INVALID_IX; - } - return JSONSuccess; -error: - parson_free(object->cells); - parson_free(object->names); - parson_free(object->values); - parson_free(object->cell_ixs); - parson_free(object->hashes); - return JSONFailure; -} - -static void json_object_deinit(JSON_Object *object, parson_bool_t free_keys, parson_bool_t free_values) { - unsigned int i = 0; - for (i = 0; i < object->count; i++) { - if (free_keys) { - parson_free(object->names[i]); - } - if (free_values) { - json_value_free(object->values[i]); - } - } - - object->count = 0; - object->item_capacity = 0; - object->cell_capacity = 0; - - parson_free(object->cells); - parson_free(object->names); - parson_free(object->values); - parson_free(object->cell_ixs); - parson_free(object->hashes); - - object->cells = NULL; - object->names = NULL; - object->values = NULL; - object->cell_ixs = NULL; - object->hashes = NULL; -} - -static JSON_Status json_object_grow_and_rehash(JSON_Object *object) { - JSON_Value *wrapping_value = NULL; - JSON_Object new_object; - char *key = NULL; - JSON_Value *value = NULL; - unsigned int i = 0; - size_t new_capacity = MAX(object->cell_capacity * 2, STARTING_CAPACITY); - JSON_Status res = json_object_init(&new_object, new_capacity); - if (res != JSONSuccess) { - return JSONFailure; - } - - wrapping_value = json_object_get_wrapping_value(object); - new_object.wrapping_value = wrapping_value; - - for (i = 0; i < object->count; i++) { - key = object->names[i]; - value = object->values[i]; - res = json_object_add(&new_object, key, value); - if (res != JSONSuccess) { - json_object_deinit(&new_object, PARSON_FALSE, PARSON_FALSE); - return JSONFailure; - } - value->parent = wrapping_value; - } - json_object_deinit(object, PARSON_FALSE, PARSON_FALSE); - *object = new_object; - return JSONSuccess; -} - -static size_t json_object_get_cell_ix(const JSON_Object *object, const char *key, size_t key_len, unsigned long hash, parson_bool_t *out_found) { - size_t cell_ix = hash & (object->cell_capacity - 1); - size_t cell = 0; - size_t ix = 0; - unsigned int i = 0; - unsigned long hash_to_check = 0; - const char *key_to_check = NULL; - size_t key_to_check_len = 0; - - *out_found = PARSON_FALSE; - - for (i = 0; i < object->cell_capacity; i++) { - ix = (cell_ix + i) & (object->cell_capacity - 1); - cell = object->cells[ix]; - if (cell == OBJECT_INVALID_IX) { - return ix; - } - hash_to_check = object->hashes[cell]; - if (hash != hash_to_check) { - continue; - } - key_to_check = object->names[cell]; - key_to_check_len = strlen(key_to_check); - if (key_to_check_len == key_len && strncmp(key, key_to_check, key_len) == 0) { - *out_found = PARSON_TRUE; - return ix; - } - } - return OBJECT_INVALID_IX; -} - -static JSON_Status json_object_add(JSON_Object *object, char *name, JSON_Value *value) { - unsigned long hash = 0; - parson_bool_t found = PARSON_FALSE; - size_t cell_ix = 0; - JSON_Status res = JSONFailure; - - if (!object || !name || !value) { - return JSONFailure; - } - - hash = hash_string(name, strlen(name)); - found = PARSON_FALSE; - cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found); - if (found) { - return JSONFailure; - } - - if (object->count >= object->item_capacity) { - res = json_object_grow_and_rehash(object); - if (res != JSONSuccess) { - return JSONFailure; - } - cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found); - } - - object->names[object->count] = name; - object->cells[cell_ix] = object->count; - object->values[object->count] = value; - object->cell_ixs[object->count] = cell_ix; - object->hashes[object->count] = hash; - object->count++; - value->parent = json_object_get_wrapping_value(object); - - return JSONSuccess; -} - -static JSON_Value *json_object_getn_value(const JSON_Object *object, const char *name, size_t name_len) { - unsigned long hash = 0; - parson_bool_t found = PARSON_FALSE; - size_t cell_ix = 0; - size_t item_ix = 0; - if (!object || !name) { - return NULL; - } - hash = hash_string(name, name_len); - found = PARSON_FALSE; - cell_ix = json_object_get_cell_ix(object, name, name_len, hash, &found); - if (!found) { - return NULL; - } - item_ix = object->cells[cell_ix]; - return object->values[item_ix]; -} - -static JSON_Status json_object_remove_internal(JSON_Object *object, const char *name, parson_bool_t free_value) { - unsigned long hash = 0; - parson_bool_t found = PARSON_FALSE; - size_t cell = 0; - size_t item_ix = 0; - size_t last_item_ix = 0; - size_t i = 0; - size_t j = 0; - size_t x = 0; - size_t k = 0; - JSON_Value *val = NULL; - - if (object == NULL) { - return JSONFailure; - } - - hash = hash_string(name, strlen(name)); - found = PARSON_FALSE; - cell = json_object_get_cell_ix(object, name, strlen(name), hash, &found); - if (!found) { - return JSONFailure; - } - - item_ix = object->cells[cell]; - if (free_value) { - val = object->values[item_ix]; - json_value_free(val); - val = NULL; - } - - parson_free(object->names[item_ix]); - last_item_ix = object->count - 1; - if (item_ix < last_item_ix) { - object->names[item_ix] = object->names[last_item_ix]; - object->values[item_ix] = object->values[last_item_ix]; - object->cell_ixs[item_ix] = object->cell_ixs[last_item_ix]; - object->hashes[item_ix] = object->hashes[last_item_ix]; - object->cells[object->cell_ixs[item_ix]] = item_ix; - } - object->count--; - - i = cell; - j = i; - for (x = 0; x < (object->cell_capacity - 1); x++) { - j = (j + 1) & (object->cell_capacity - 1); - if (object->cells[j] == OBJECT_INVALID_IX) { - break; - } - k = object->hashes[object->cells[j]] & (object->cell_capacity - 1); - if ((j > i && (k <= i || k > j)) || (j < i && (k <= i && k > j))) { - object->cell_ixs[object->cells[j]] = i; - object->cells[i] = object->cells[j]; - i = j; - } - } - object->cells[i] = OBJECT_INVALID_IX; - return JSONSuccess; -} - -static JSON_Status json_object_dotremove_internal(JSON_Object *object, const char *name, parson_bool_t free_value) { - JSON_Value *temp_value = NULL; - JSON_Object *temp_object = NULL; - const char *dot_pos = strchr(name, '.'); - if (!dot_pos) { - return json_object_remove_internal(object, name, free_value); - } - temp_value = json_object_getn_value(object, name, dot_pos - name); - if (json_value_get_type(temp_value) != JSONObject) { - return JSONFailure; - } - temp_object = json_value_get_object(temp_value); - return json_object_dotremove_internal(temp_object, dot_pos + 1, free_value); -} - -static void json_object_free(JSON_Object *object) { - json_object_deinit(object, PARSON_TRUE, PARSON_TRUE); - parson_free(object); -} - -/* JSON Array */ -static JSON_Array *json_array_make(JSON_Value *wrapping_value) { - JSON_Array *new_array = (JSON_Array *)parson_malloc(sizeof(JSON_Array)); - if (new_array == NULL) { - return NULL; - } - new_array->wrapping_value = wrapping_value; - new_array->items = (JSON_Value **)NULL; - new_array->capacity = 0; - new_array->count = 0; - return new_array; -} - -static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) { - if (array->count >= array->capacity) { - size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY); - if (json_array_resize(array, new_capacity) != JSONSuccess) { - return JSONFailure; - } - } - value->parent = json_array_get_wrapping_value(array); - array->items[array->count] = value; - array->count++; - return JSONSuccess; -} - -static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) { - JSON_Value **new_items = NULL; - if (new_capacity == 0) { - return JSONFailure; - } - new_items = (JSON_Value **)parson_malloc(new_capacity * sizeof(JSON_Value *)); - if (new_items == NULL) { - return JSONFailure; - } - if (array->items != NULL && array->count > 0) { - memcpy(new_items, array->items, array->count * sizeof(JSON_Value *)); - } - parson_free(array->items); - array->items = new_items; - array->capacity = new_capacity; - return JSONSuccess; -} - -static void json_array_free(JSON_Array *array) { - size_t i; - for (i = 0; i < array->count; i++) { - json_value_free(array->items[i]); - } - parson_free(array->items); - parson_free(array); -} - -/* JSON Value */ -static JSON_Value *json_value_init_string_no_copy(char *string, size_t length) { - JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { - return NULL; - } - new_value->parent = NULL; - new_value->type = JSONString; - new_value->value.string.chars = string; - new_value->value.string.length = length; - return new_value; -} - -/* Parser */ -static JSON_Status skip_quotes(const char **string) { - if (**string != '\"') { - return JSONFailure; - } - SKIP_CHAR(string); - while (**string != '\"') { - if (**string == '\0') { - return JSONFailure; - } else if (**string == '\\') { - SKIP_CHAR(string); - if (**string == '\0') { - return JSONFailure; - } - } - SKIP_CHAR(string); - } - SKIP_CHAR(string); - return JSONSuccess; -} - -static JSON_Status parse_utf16(const char **unprocessed, char **processed) { - unsigned int cp, lead, trail; - char *processed_ptr = *processed; - const char *unprocessed_ptr = *unprocessed; - JSON_Status status = JSONFailure; - unprocessed_ptr++; /* skips u */ - status = parse_utf16_hex(unprocessed_ptr, &cp); - if (status != JSONSuccess) { - return JSONFailure; - } - if (cp < 0x80) { - processed_ptr[0] = (char)cp; /* 0xxxxxxx */ - } else if (cp < 0x800) { - processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */ - processed_ptr[1] = ((cp)&0x3F) | 0x80; /* 10xxxxxx */ - processed_ptr += 1; - } else if (cp < 0xD800 || cp > 0xDFFF) { - processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */ - processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */ - processed_ptr[2] = ((cp)&0x3F) | 0x80; /* 10xxxxxx */ - processed_ptr += 2; - } else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */ - lead = cp; - unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */ - if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u') { - return JSONFailure; - } - status = parse_utf16_hex(unprocessed_ptr, &trail); - if (status != JSONSuccess || trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */ - return JSONFailure; - } - cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000; - processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */ - processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */ - processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */ - processed_ptr[3] = (((cp)&0x3F) | 0x80); /* 10xxxxxx */ - processed_ptr += 3; - } else { /* trail surrogate before lead surrogate */ - return JSONFailure; - } - unprocessed_ptr += 3; - *processed = processed_ptr; - *unprocessed = unprocessed_ptr; - return JSONSuccess; -} - -/* Copies and processes passed string up to supplied length. -Example: "\u006Corem ipsum" -> lorem ipsum */ -static char *process_string(const char *input, size_t input_len, size_t *output_len) { - const char *input_ptr = input; - size_t initial_size = (input_len + 1) * sizeof(char); - size_t final_size = 0; - char *output = NULL, *output_ptr = NULL, *resized_output = NULL; - output = (char *)parson_malloc(initial_size); - if (output == NULL) { - goto error; - } - output_ptr = output; - while ((*input_ptr != '\0') && (size_t)(input_ptr - input) < input_len) { - if (*input_ptr == '\\') { - input_ptr++; - switch (*input_ptr) { - case '\"': - *output_ptr = '\"'; - break; - case '\\': - *output_ptr = '\\'; - break; - case '/': - *output_ptr = '/'; - break; - case 'b': - *output_ptr = '\b'; - break; - case 'f': - *output_ptr = '\f'; - break; - case 'n': - *output_ptr = '\n'; - break; - case 'r': - *output_ptr = '\r'; - break; - case 't': - *output_ptr = '\t'; - break; - case 'u': - if (parse_utf16(&input_ptr, &output_ptr) != JSONSuccess) { - goto error; - } - break; - default: - goto error; - } - } else if ((unsigned char)*input_ptr < 0x20) { - goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */ - } else { - *output_ptr = *input_ptr; - } - output_ptr++; - input_ptr++; - } - *output_ptr = '\0'; - /* resize to new length */ - final_size = (size_t)(output_ptr - output) + 1; - /* todo: don't resize if final_size == initial_size */ - resized_output = (char *)parson_malloc(final_size); - if (resized_output == NULL) { - goto error; - } - memcpy(resized_output, output, final_size); - *output_len = final_size - 1; - parson_free(output); - return resized_output; -error: - parson_free(output); - return NULL; -} - -/* Return processed contents of a string between quotes and - skips passed argument to a matching quote. */ -static char *get_quoted_string(const char **string, size_t *output_string_len) { - const char *string_start = *string; - size_t input_string_len = 0; - JSON_Status status = skip_quotes(string); - if (status != JSONSuccess) { - return NULL; - } - input_string_len = *string - string_start - 2; /* length without quotes */ - return process_string(string_start + 1, input_string_len, output_string_len); -} - -static JSON_Value *parse_value(const char **string, size_t nesting) { - if (nesting > MAX_NESTING) { - return NULL; - } - SKIP_WHITESPACES(string); - switch (**string) { - case '{': - return parse_object_value(string, nesting + 1); - case '[': - return parse_array_value(string, nesting + 1); - case '\"': - return parse_string_value(string); - case 'f': - case 't': - return parse_boolean_value(string); - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return parse_number_value(string); - case 'n': - return parse_null_value(string); - default: - return NULL; - } -} - -static JSON_Value *parse_object_value(const char **string, size_t nesting) { - JSON_Status status = JSONFailure; - JSON_Value *output_value = NULL, *new_value = NULL; - JSON_Object *output_object = NULL; - char *new_key = NULL; - - output_value = json_value_init_object(); - if (output_value == NULL) { - return NULL; - } - if (**string != '{') { - json_value_free(output_value); - return NULL; - } - output_object = json_value_get_object(output_value); - SKIP_CHAR(string); - SKIP_WHITESPACES(string); - if (**string == '}') { /* empty object */ - SKIP_CHAR(string); - return output_value; - } - while (**string != '\0') { - size_t key_len = 0; - new_key = get_quoted_string(string, &key_len); - /* We do not support key names with embedded \0 chars */ - if (!new_key) { - json_value_free(output_value); - return NULL; - } - if (key_len != strlen(new_key)) { - parson_free(new_key); - json_value_free(output_value); - return NULL; - } - SKIP_WHITESPACES(string); - if (**string != ':') { - parson_free(new_key); - json_value_free(output_value); - return NULL; - } - SKIP_CHAR(string); - new_value = parse_value(string, nesting); - if (new_value == NULL) { - parson_free(new_key); - json_value_free(output_value); - return NULL; - } - status = json_object_add(output_object, new_key, new_value); - if (status != JSONSuccess) { - parson_free(new_key); - json_value_free(new_value); - json_value_free(output_value); - return NULL; - } - SKIP_WHITESPACES(string); - if (**string != ',') { - break; - } - SKIP_CHAR(string); - SKIP_WHITESPACES(string); - if (**string == '}') { - break; - } - } - SKIP_WHITESPACES(string); - if (**string != '}') { - json_value_free(output_value); - return NULL; - } - SKIP_CHAR(string); - return output_value; -} - -static JSON_Value *parse_array_value(const char **string, size_t nesting) { - JSON_Value *output_value = NULL, *new_array_value = NULL; - JSON_Array *output_array = NULL; - output_value = json_value_init_array(); - if (output_value == NULL) { - return NULL; - } - if (**string != '[') { - json_value_free(output_value); - return NULL; - } - output_array = json_value_get_array(output_value); - SKIP_CHAR(string); - SKIP_WHITESPACES(string); - if (**string == ']') { /* empty array */ - SKIP_CHAR(string); - return output_value; - } - while (**string != '\0') { - new_array_value = parse_value(string, nesting); - if (new_array_value == NULL) { - json_value_free(output_value); - return NULL; - } - if (json_array_add(output_array, new_array_value) != JSONSuccess) { - json_value_free(new_array_value); - json_value_free(output_value); - return NULL; - } - SKIP_WHITESPACES(string); - if (**string != ',') { - break; - } - SKIP_CHAR(string); - SKIP_WHITESPACES(string); - if (**string == ']') { - break; - } - } - SKIP_WHITESPACES(string); - if (**string != ']' || /* Trim array after parsing is over */ - json_array_resize(output_array, json_array_get_count(output_array)) != JSONSuccess) { - json_value_free(output_value); - return NULL; - } - SKIP_CHAR(string); - return output_value; -} - -static JSON_Value *parse_string_value(const char **string) { - JSON_Value *value = NULL; - size_t new_string_len = 0; - char *new_string = get_quoted_string(string, &new_string_len); - if (new_string == NULL) { - return NULL; - } - value = json_value_init_string_no_copy(new_string, new_string_len); - if (value == NULL) { - parson_free(new_string); - return NULL; - } - return value; -} - -static JSON_Value *parse_boolean_value(const char **string) { - size_t true_token_size = SIZEOF_TOKEN("true"); - size_t false_token_size = SIZEOF_TOKEN("false"); - if (strncmp("true", *string, true_token_size) == 0) { - *string += true_token_size; - return json_value_init_boolean(1); - } else if (strncmp("false", *string, false_token_size) == 0) { - *string += false_token_size; - return json_value_init_boolean(0); - } - return NULL; -} - -static JSON_Value *parse_number_value(const char **string) { - char *end; - double number = 0; - errno = 0; - number = strtod(*string, &end); - if (errno == ERANGE && (number <= -HUGE_VAL || number >= HUGE_VAL)) { - return NULL; - } - if ((errno && errno != ERANGE) || !is_decimal(*string, end - *string)) { - return NULL; - } - *string = end; - return json_value_init_number(number); -} - -static JSON_Value *parse_null_value(const char **string) { - size_t token_size = SIZEOF_TOKEN("null"); - if (strncmp("null", *string, token_size) == 0) { - *string += token_size; - return json_value_init_null(); - } - return NULL; -} - -/* Serialization */ -#define APPEND_STRING(str) \ - do { \ - written = append_string(buf, (str)); \ - if (written < 0) { \ - return -1; \ - } \ - if (buf != NULL) { \ - buf += written; \ - } \ - written_total += written; \ - } while (0) - -#define APPEND_INDENT(level) \ - do { \ - written = append_indent(buf, (level)); \ - if (written < 0) { \ - return -1; \ - } \ - if (buf != NULL) { \ - buf += written; \ - } \ - written_total += written; \ - } while (0) - -static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf) { - const char *key = NULL, *string = NULL; - JSON_Value *temp_value = NULL; - JSON_Array *array = NULL; - JSON_Object *object = NULL; - size_t i = 0, count = 0; - double num = 0.0; - int written = -1, written_total = 0; - size_t len = 0; - - switch (json_value_get_type(value)) { - case JSONArray: - array = json_value_get_array(value); - count = json_array_get_count(array); - APPEND_STRING("["); - if (count > 0 && is_pretty) { - APPEND_STRING("\n"); - } - for (i = 0; i < count; i++) { - if (is_pretty) { - APPEND_INDENT(level + 1); - } - temp_value = json_array_get_value(array, i); - written = json_serialize_to_buffer_r(temp_value, buf, level + 1, is_pretty, num_buf); - if (written < 0) { - return -1; - } - if (buf != NULL) { - buf += written; - } - written_total += written; - if (i < (count - 1)) { - APPEND_STRING(","); - } - if (is_pretty) { - APPEND_STRING("\n"); - } - } - if (count > 0 && is_pretty) { - APPEND_INDENT(level); - } - APPEND_STRING("]"); - return written_total; - case JSONObject: - object = json_value_get_object(value); - count = json_object_get_count(object); - APPEND_STRING("{"); - if (count > 0 && is_pretty) { - APPEND_STRING("\n"); - } - for (i = 0; i < count; i++) { - key = json_object_get_name(object, i); - if (key == NULL) { - return -1; - } - if (is_pretty) { - APPEND_INDENT(level + 1); - } - /* We do not support key names with embedded \0 chars */ - written = json_serialize_string(key, strlen(key), buf); - if (written < 0) { - return -1; - } - if (buf != NULL) { - buf += written; - } - written_total += written; - APPEND_STRING(":"); - if (is_pretty) { - APPEND_STRING(" "); - } - temp_value = json_object_get_value_at(object, i); - written = json_serialize_to_buffer_r(temp_value, buf, level + 1, is_pretty, num_buf); - if (written < 0) { - return -1; - } - if (buf != NULL) { - buf += written; - } - written_total += written; - if (i < (count - 1)) { - APPEND_STRING(","); - } - if (is_pretty) { - APPEND_STRING("\n"); - } - } - if (count > 0 && is_pretty) { - APPEND_INDENT(level); - } - APPEND_STRING("}"); - return written_total; - case JSONString: - string = json_value_get_string(value); - if (string == NULL) { - return -1; - } - len = json_value_get_string_len(value); - written = json_serialize_string(string, len, buf); - if (written < 0) { - return -1; - } - if (buf != NULL) { - buf += written; - } - written_total += written; - return written_total; - case JSONBoolean: - if (json_value_get_boolean(value)) { - APPEND_STRING("true"); - } else { - APPEND_STRING("false"); - } - return written_total; - case JSONNumber: - num = json_value_get_number(value); - if (buf != NULL) { - num_buf = buf; - } - if (parson_float_format) { - written = sprintf(num_buf, parson_float_format, num); - } else { - written = sprintf(num_buf, PARSON_DEFAULT_FLOAT_FORMAT, num); - } - if (written < 0) { - return -1; - } - if (buf != NULL) { - buf += written; - } - written_total += written; - return written_total; - case JSONNull: - APPEND_STRING("null"); - return written_total; - case JSONError: - return -1; - default: - return -1; - } -} - -static int json_serialize_string(const char *string, size_t len, char *buf) { - size_t i = 0; - char c = '\0'; - int written = -1, written_total = 0; - APPEND_STRING("\""); - for (i = 0; i < len; i++) { - c = string[i]; - switch (c) { - case '\"': - APPEND_STRING("\\\""); - break; - case '\\': - APPEND_STRING("\\\\"); - break; - case '\b': - APPEND_STRING("\\b"); - break; - case '\f': - APPEND_STRING("\\f"); - break; - case '\n': - APPEND_STRING("\\n"); - break; - case '\r': - APPEND_STRING("\\r"); - break; - case '\t': - APPEND_STRING("\\t"); - break; - case '\x00': - APPEND_STRING("\\u0000"); - break; - case '\x01': - APPEND_STRING("\\u0001"); - break; - case '\x02': - APPEND_STRING("\\u0002"); - break; - case '\x03': - APPEND_STRING("\\u0003"); - break; - case '\x04': - APPEND_STRING("\\u0004"); - break; - case '\x05': - APPEND_STRING("\\u0005"); - break; - case '\x06': - APPEND_STRING("\\u0006"); - break; - case '\x07': - APPEND_STRING("\\u0007"); - break; - /* '\x08' duplicate: '\b' */ - /* '\x09' duplicate: '\t' */ - /* '\x0a' duplicate: '\n' */ - case '\x0b': - APPEND_STRING("\\u000b"); - break; - /* '\x0c' duplicate: '\f' */ - /* '\x0d' duplicate: '\r' */ - case '\x0e': - APPEND_STRING("\\u000e"); - break; - case '\x0f': - APPEND_STRING("\\u000f"); - break; - case '\x10': - APPEND_STRING("\\u0010"); - break; - case '\x11': - APPEND_STRING("\\u0011"); - break; - case '\x12': - APPEND_STRING("\\u0012"); - break; - case '\x13': - APPEND_STRING("\\u0013"); - break; - case '\x14': - APPEND_STRING("\\u0014"); - break; - case '\x15': - APPEND_STRING("\\u0015"); - break; - case '\x16': - APPEND_STRING("\\u0016"); - break; - case '\x17': - APPEND_STRING("\\u0017"); - break; - case '\x18': - APPEND_STRING("\\u0018"); - break; - case '\x19': - APPEND_STRING("\\u0019"); - break; - case '\x1a': - APPEND_STRING("\\u001a"); - break; - case '\x1b': - APPEND_STRING("\\u001b"); - break; - case '\x1c': - APPEND_STRING("\\u001c"); - break; - case '\x1d': - APPEND_STRING("\\u001d"); - break; - case '\x1e': - APPEND_STRING("\\u001e"); - break; - case '\x1f': - APPEND_STRING("\\u001f"); - break; - case '/': - if (parson_escape_slashes) { - APPEND_STRING("\\/"); /* to make json embeddable in xml\/html */ - } else { - APPEND_STRING("/"); - } - break; - default: - if (buf != NULL) { - buf[0] = c; - buf += 1; - } - written_total += 1; - break; - } - } - APPEND_STRING("\""); - return written_total; -} - -static int append_indent(char *buf, int level) { - int i; - int written = -1, written_total = 0; - for (i = 0; i < level; i++) { - APPEND_STRING(" "); - } - return written_total; -} - -static int append_string(char *buf, const char *string) { - if (buf == NULL) { - return (int)strlen(string); - } - return sprintf(buf, "%s", string); -} - -#undef APPEND_STRING -#undef APPEND_INDENT - -/* Parser API */ -JSON_Value *json_parse_file(const char *filename) { - char *file_contents = read_file(filename); - JSON_Value *output_value = NULL; - if (file_contents == NULL) { - return NULL; - } - output_value = json_parse_string(file_contents); - parson_free(file_contents); - return output_value; -} - -JSON_Value *json_parse_file_with_comments(const char *filename) { - char *file_contents = read_file(filename); - JSON_Value *output_value = NULL; - if (file_contents == NULL) { - return NULL; - } - output_value = json_parse_string_with_comments(file_contents); - parson_free(file_contents); - return output_value; -} - -JSON_Value *json_parse_string(const char *string) { - if (string == NULL) { - return NULL; - } - if (string[0] == '\xEF' && string[1] == '\xBB' && string[2] == '\xBF') { - string = string + 3; /* Support for UTF-8 BOM */ - } - return parse_value((const char **)&string, 0); -} - -JSON_Value *json_parse_string_with_comments(const char *string) { - JSON_Value *result = NULL; - char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL; - string_mutable_copy = parson_strdup(string); - if (string_mutable_copy == NULL) { - return NULL; - } - remove_comments(string_mutable_copy, "/*", "*/"); - remove_comments(string_mutable_copy, "//", "\n"); - string_mutable_copy_ptr = string_mutable_copy; - result = parse_value((const char **)&string_mutable_copy_ptr, 0); - parson_free(string_mutable_copy); - return result; -} - -/* JSON Object API */ - -JSON_Value *json_object_get_value(const JSON_Object *object, const char *name) { - if (object == NULL || name == NULL) { - return NULL; - } - return json_object_getn_value(object, name, strlen(name)); -} - -const char *json_object_get_string(const JSON_Object *object, const char *name) { - return json_value_get_string(json_object_get_value(object, name)); -} - -size_t json_object_get_string_len(const JSON_Object *object, const char *name) { - return json_value_get_string_len(json_object_get_value(object, name)); -} - -double json_object_get_number(const JSON_Object *object, const char *name) { - return json_value_get_number(json_object_get_value(object, name)); -} - -JSON_Object *json_object_get_object(const JSON_Object *object, const char *name) { - return json_value_get_object(json_object_get_value(object, name)); -} - -JSON_Array *json_object_get_array(const JSON_Object *object, const char *name) { - return json_value_get_array(json_object_get_value(object, name)); -} - -int json_object_get_boolean(const JSON_Object *object, const char *name) { - return json_value_get_boolean(json_object_get_value(object, name)); -} - -JSON_Value *json_object_dotget_value(const JSON_Object *object, const char *name) { - const char *dot_position = strchr(name, '.'); - if (!dot_position) { - return json_object_get_value(object, name); - } - object = json_value_get_object(json_object_getn_value(object, name, dot_position - name)); - return json_object_dotget_value(object, dot_position + 1); -} - -const char *json_object_dotget_string(const JSON_Object *object, const char *name) { - return json_value_get_string(json_object_dotget_value(object, name)); -} - -size_t json_object_dotget_string_len(const JSON_Object *object, const char *name) { - return json_value_get_string_len(json_object_dotget_value(object, name)); -} - -double json_object_dotget_number(const JSON_Object *object, const char *name) { - return json_value_get_number(json_object_dotget_value(object, name)); -} - -JSON_Object *json_object_dotget_object(const JSON_Object *object, const char *name) { - return json_value_get_object(json_object_dotget_value(object, name)); -} - -JSON_Array *json_object_dotget_array(const JSON_Object *object, const char *name) { - return json_value_get_array(json_object_dotget_value(object, name)); -} - -int json_object_dotget_boolean(const JSON_Object *object, const char *name) { - return json_value_get_boolean(json_object_dotget_value(object, name)); -} - -size_t json_object_get_count(const JSON_Object *object) { - return object ? object->count : 0; -} - -const char *json_object_get_name(const JSON_Object *object, size_t index) { - if (object == NULL || index >= json_object_get_count(object)) { - return NULL; - } - return object->names[index]; -} - -JSON_Value *json_object_get_value_at(const JSON_Object *object, size_t index) { - if (object == NULL || index >= json_object_get_count(object)) { - return NULL; - } - return object->values[index]; -} - -JSON_Value *json_object_get_wrapping_value(const JSON_Object *object) { - if (!object) { - return NULL; - } - return object->wrapping_value; -} - -int json_object_has_value(const JSON_Object *object, const char *name) { - return json_object_get_value(object, name) != NULL; -} - -int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) { - JSON_Value *val = json_object_get_value(object, name); - return val != NULL && json_value_get_type(val) == type; -} - -int json_object_dothas_value(const JSON_Object *object, const char *name) { - return json_object_dotget_value(object, name) != NULL; -} - -int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) { - JSON_Value *val = json_object_dotget_value(object, name); - return val != NULL && json_value_get_type(val) == type; -} - -/* JSON Array API */ -JSON_Value *json_array_get_value(const JSON_Array *array, size_t index) { - if (array == NULL || index >= json_array_get_count(array)) { - return NULL; - } - return array->items[index]; -} - -const char *json_array_get_string(const JSON_Array *array, size_t index) { - return json_value_get_string(json_array_get_value(array, index)); -} - -size_t json_array_get_string_len(const JSON_Array *array, size_t index) { - return json_value_get_string_len(json_array_get_value(array, index)); -} - -double json_array_get_number(const JSON_Array *array, size_t index) { - return json_value_get_number(json_array_get_value(array, index)); -} - -JSON_Object *json_array_get_object(const JSON_Array *array, size_t index) { - return json_value_get_object(json_array_get_value(array, index)); -} - -JSON_Array *json_array_get_array(const JSON_Array *array, size_t index) { - return json_value_get_array(json_array_get_value(array, index)); -} - -int json_array_get_boolean(const JSON_Array *array, size_t index) { - return json_value_get_boolean(json_array_get_value(array, index)); -} - -size_t json_array_get_count(const JSON_Array *array) { - return array ? array->count : 0; -} - -JSON_Value *json_array_get_wrapping_value(const JSON_Array *array) { - if (!array) { - return NULL; - } - return array->wrapping_value; -} - -/* JSON Value API */ -JSON_Value_Type json_value_get_type(const JSON_Value *value) { - return value ? value->type : JSONError; -} - -JSON_Object *json_value_get_object(const JSON_Value *value) { - return json_value_get_type(value) == JSONObject ? value->value.object : NULL; -} - -JSON_Array *json_value_get_array(const JSON_Value *value) { - return json_value_get_type(value) == JSONArray ? value->value.array : NULL; -} - -static const JSON_String *json_value_get_string_desc(const JSON_Value *value) { - return json_value_get_type(value) == JSONString ? &value->value.string : NULL; -} - -const char *json_value_get_string(const JSON_Value *value) { - const JSON_String *str = json_value_get_string_desc(value); - return str ? str->chars : NULL; -} - -size_t json_value_get_string_len(const JSON_Value *value) { - const JSON_String *str = json_value_get_string_desc(value); - return str ? str->length : 0; -} - -double json_value_get_number(const JSON_Value *value) { - return json_value_get_type(value) == JSONNumber ? value->value.number : 0; -} - -int json_value_get_boolean(const JSON_Value *value) { - return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1; -} - -JSON_Value *json_value_get_parent(const JSON_Value *value) { - return value ? value->parent : NULL; -} - -void json_value_free(JSON_Value *value) { - switch (json_value_get_type(value)) { - case JSONObject: - json_object_free(value->value.object); - break; - case JSONString: - parson_free(value->value.string.chars); - break; - case JSONArray: - json_array_free(value->value.array); - break; - default: - break; - } - parson_free(value); -} - -JSON_Value *json_value_init_object(void) { - JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { - return NULL; - } - new_value->parent = NULL; - new_value->type = JSONObject; - new_value->value.object = json_object_make(new_value); - if (!new_value->value.object) { - parson_free(new_value); - return NULL; - } - return new_value; -} - -JSON_Value *json_value_init_array(void) { - JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { - return NULL; - } - new_value->parent = NULL; - new_value->type = JSONArray; - new_value->value.array = json_array_make(new_value); - if (!new_value->value.array) { - parson_free(new_value); - return NULL; - } - return new_value; -} - -JSON_Value *json_value_init_string(const char *string) { - if (string == NULL) { - return NULL; - } - return json_value_init_string_with_len(string, strlen(string)); -} - -JSON_Value *json_value_init_string_with_len(const char *string, size_t length) { - char *copy = NULL; - JSON_Value *value; - if (string == NULL) { - return NULL; - } - if (!is_valid_utf8(string, length)) { - return NULL; - } - copy = parson_strndup(string, length); - if (copy == NULL) { - return NULL; - } - value = json_value_init_string_no_copy(copy, length); - if (value == NULL) { - parson_free(copy); - } - return value; -} - -JSON_Value *json_value_init_number(double number) { - JSON_Value *new_value = NULL; - if (IS_NUMBER_INVALID(number)) { - return NULL; - } - new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value)); - if (new_value == NULL) { - return NULL; - } - new_value->parent = NULL; - new_value->type = JSONNumber; - new_value->value.number = number; - return new_value; -} - -JSON_Value *json_value_init_boolean(int boolean) { - JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { - return NULL; - } - new_value->parent = NULL; - new_value->type = JSONBoolean; - new_value->value.boolean = boolean ? 1 : 0; - return new_value; -} - -JSON_Value *json_value_init_null(void) { - JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { - return NULL; - } - new_value->parent = NULL; - new_value->type = JSONNull; - return new_value; -} - -JSON_Value *json_value_deep_copy(const JSON_Value *value) { - size_t i = 0; - JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL; - const JSON_String *temp_string = NULL; - const char *temp_key = NULL; - char *temp_string_copy = NULL; - JSON_Array *temp_array = NULL, *temp_array_copy = NULL; - JSON_Object *temp_object = NULL, *temp_object_copy = NULL; - JSON_Status res = JSONFailure; - char *key_copy = NULL; - - switch (json_value_get_type(value)) { - case JSONArray: - temp_array = json_value_get_array(value); - return_value = json_value_init_array(); - if (return_value == NULL) { - return NULL; - } - temp_array_copy = json_value_get_array(return_value); - for (i = 0; i < json_array_get_count(temp_array); i++) { - temp_value = json_array_get_value(temp_array, i); - temp_value_copy = json_value_deep_copy(temp_value); - if (temp_value_copy == NULL) { - json_value_free(return_value); - return NULL; - } - if (json_array_add(temp_array_copy, temp_value_copy) != JSONSuccess) { - json_value_free(return_value); - json_value_free(temp_value_copy); - return NULL; - } - } - return return_value; - case JSONObject: - temp_object = json_value_get_object(value); - return_value = json_value_init_object(); - if (!return_value) { - return NULL; - } - temp_object_copy = json_value_get_object(return_value); - for (i = 0; i < json_object_get_count(temp_object); i++) { - temp_key = json_object_get_name(temp_object, i); - temp_value = json_object_get_value(temp_object, temp_key); - temp_value_copy = json_value_deep_copy(temp_value); - if (!temp_value_copy) { - json_value_free(return_value); - return NULL; - } - key_copy = parson_strdup(temp_key); - if (!key_copy) { - json_value_free(temp_value_copy); - json_value_free(return_value); - return NULL; - } - res = json_object_add(temp_object_copy, key_copy, temp_value_copy); - if (res != JSONSuccess) { - parson_free(key_copy); - json_value_free(temp_value_copy); - json_value_free(return_value); - return NULL; - } - } - return return_value; - case JSONBoolean: - return json_value_init_boolean(json_value_get_boolean(value)); - case JSONNumber: - return json_value_init_number(json_value_get_number(value)); - case JSONString: - temp_string = json_value_get_string_desc(value); - if (temp_string == NULL) { - return NULL; - } - temp_string_copy = parson_strndup(temp_string->chars, temp_string->length); - if (temp_string_copy == NULL) { - return NULL; - } - return_value = json_value_init_string_no_copy(temp_string_copy, temp_string->length); - if (return_value == NULL) { - parson_free(temp_string_copy); - } - return return_value; - case JSONNull: - return json_value_init_null(); - case JSONError: - return NULL; - default: - return NULL; - } -} - -size_t json_serialization_size(const JSON_Value *value) { - char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ - int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_FALSE, num_buf); - return res < 0 ? 0 : (size_t)(res) + 1; -} - -JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { - int written = -1; - size_t needed_size_in_bytes = json_serialization_size(value); - if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) { - return JSONFailure; - } - written = json_serialize_to_buffer_r(value, buf, 0, PARSON_FALSE, NULL); - if (written < 0) { - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename) { - JSON_Status return_code = JSONSuccess; - FILE *fp = NULL; - char *serialized_string = json_serialize_to_string(value); - if (serialized_string == NULL) { - return JSONFailure; - } - fp = fopen(filename, "w"); - if (fp == NULL) { - json_free_serialized_string(serialized_string); - return JSONFailure; - } - if (fputs(serialized_string, fp) == EOF) { - return_code = JSONFailure; - } - if (fclose(fp) == EOF) { - return_code = JSONFailure; - } - json_free_serialized_string(serialized_string); - return return_code; -} - -char *json_serialize_to_string(const JSON_Value *value) { - JSON_Status serialization_result = JSONFailure; - size_t buf_size_bytes = json_serialization_size(value); - char *buf = NULL; - if (buf_size_bytes == 0) { - return NULL; - } - buf = (char *)parson_malloc(buf_size_bytes); - if (buf == NULL) { - return NULL; - } - serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes); - if (serialization_result != JSONSuccess) { - json_free_serialized_string(buf); - return NULL; - } - return buf; -} - -size_t json_serialization_size_pretty(const JSON_Value *value) { - char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ - int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_TRUE, num_buf); - return res < 0 ? 0 : (size_t)(res) + 1; -} - -JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { - int written = -1; - size_t needed_size_in_bytes = json_serialization_size_pretty(value); - if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) { - return JSONFailure; - } - written = json_serialize_to_buffer_r(value, buf, 0, PARSON_TRUE, NULL); - if (written < 0) { - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename) { - JSON_Status return_code = JSONSuccess; - FILE *fp = NULL; - char *serialized_string = json_serialize_to_string_pretty(value); - if (serialized_string == NULL) { - return JSONFailure; - } - fp = fopen(filename, "w"); - if (fp == NULL) { - json_free_serialized_string(serialized_string); - return JSONFailure; - } - if (fputs(serialized_string, fp) == EOF) { - return_code = JSONFailure; - } - if (fclose(fp) == EOF) { - return_code = JSONFailure; - } - json_free_serialized_string(serialized_string); - return return_code; -} - -char *json_serialize_to_string_pretty(const JSON_Value *value) { - JSON_Status serialization_result = JSONFailure; - size_t buf_size_bytes = json_serialization_size_pretty(value); - char *buf = NULL; - if (buf_size_bytes == 0) { - return NULL; - } - buf = (char *)parson_malloc(buf_size_bytes); - if (buf == NULL) { - return NULL; - } - serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes); - if (serialization_result != JSONSuccess) { - json_free_serialized_string(buf); - return NULL; - } - return buf; -} - -void json_free_serialized_string(char *string) { - parson_free(string); -} - -JSON_Status json_array_remove(JSON_Array *array, size_t ix) { - size_t to_move_bytes = 0; - if (array == NULL || ix >= json_array_get_count(array)) { - return JSONFailure; - } - json_value_free(json_array_get_value(array, ix)); - to_move_bytes = (json_array_get_count(array) - 1 - ix) * sizeof(JSON_Value *); - memmove(array->items + ix, array->items + ix + 1, to_move_bytes); - array->count -= 1; - return JSONSuccess; -} - -JSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) { - if (array == NULL || value == NULL || value->parent != NULL || ix >= json_array_get_count(array)) { - return JSONFailure; - } - json_value_free(json_array_get_value(array, ix)); - value->parent = json_array_get_wrapping_value(array); - array->items[ix] = value; - return JSONSuccess; -} - -JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char *string) { - JSON_Value *value = json_value_init_string(string); - if (value == NULL) { - return JSONFailure; - } - if (json_array_replace_value(array, i, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i, const char *string, size_t len) { - JSON_Value *value = json_value_init_string_with_len(string, len); - if (value == NULL) { - return JSONFailure; - } - if (json_array_replace_value(array, i, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number) { - JSON_Value *value = json_value_init_number(number); - if (value == NULL) { - return JSONFailure; - } - if (json_array_replace_value(array, i, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean) { - JSON_Value *value = json_value_init_boolean(boolean); - if (value == NULL) { - return JSONFailure; - } - if (json_array_replace_value(array, i, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_replace_null(JSON_Array *array, size_t i) { - JSON_Value *value = json_value_init_null(); - if (value == NULL) { - return JSONFailure; - } - if (json_array_replace_value(array, i, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_clear(JSON_Array *array) { - size_t i = 0; - if (array == NULL) { - return JSONFailure; - } - for (i = 0; i < json_array_get_count(array); i++) { - json_value_free(json_array_get_value(array, i)); - } - array->count = 0; - return JSONSuccess; -} - -JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) { - if (array == NULL || value == NULL || value->parent != NULL) { - return JSONFailure; - } - return json_array_add(array, value); -} - -JSON_Status json_array_append_string(JSON_Array *array, const char *string) { - JSON_Value *value = json_value_init_string(string); - if (value == NULL) { - return JSONFailure; - } - if (json_array_append_value(array, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_append_string_with_len(JSON_Array *array, const char *string, size_t len) { - JSON_Value *value = json_value_init_string_with_len(string, len); - if (value == NULL) { - return JSONFailure; - } - if (json_array_append_value(array, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_append_number(JSON_Array *array, double number) { - JSON_Value *value = json_value_init_number(number); - if (value == NULL) { - return JSONFailure; - } - if (json_array_append_value(array, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_append_boolean(JSON_Array *array, int boolean) { - JSON_Value *value = json_value_init_boolean(boolean); - if (value == NULL) { - return JSONFailure; - } - if (json_array_append_value(array, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_array_append_null(JSON_Array *array) { - JSON_Value *value = json_value_init_null(); - if (value == NULL) { - return JSONFailure; - } - if (json_array_append_value(array, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) { - unsigned long hash = 0; - parson_bool_t found = PARSON_FALSE; - size_t cell_ix = 0; - size_t item_ix = 0; - JSON_Value *old_value = NULL; - char *key_copy = NULL; - - if (!object || !name || !value || value->parent) { - return JSONFailure; - } - hash = hash_string(name, strlen(name)); - found = PARSON_FALSE; - cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found); - if (found) { - item_ix = object->cells[cell_ix]; - old_value = object->values[item_ix]; - json_value_free(old_value); - object->values[item_ix] = value; - value->parent = json_object_get_wrapping_value(object); - return JSONSuccess; - } - if (object->count >= object->item_capacity) { - JSON_Status res = json_object_grow_and_rehash(object); - if (res != JSONSuccess) { - return JSONFailure; - } - cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found); - } - key_copy = parson_strdup(name); - if (!key_copy) { - return JSONFailure; - } - object->names[object->count] = key_copy; - object->cells[cell_ix] = object->count; - object->values[object->count] = value; - object->cell_ixs[object->count] = cell_ix; - object->hashes[object->count] = hash; - object->count++; - value->parent = json_object_get_wrapping_value(object); - return JSONSuccess; -} - -JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) { - JSON_Value *value = json_value_init_string(string); - JSON_Status status = json_object_set_value(object, name, value); - if (status != JSONSuccess) { - json_value_free(value); - } - return status; -} - -JSON_Status json_object_set_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len) { - JSON_Value *value = json_value_init_string_with_len(string, len); - JSON_Status status = json_object_set_value(object, name, value); - if (status != JSONSuccess) { - json_value_free(value); - } - return status; -} - -JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) { - JSON_Value *value = json_value_init_number(number); - JSON_Status status = json_object_set_value(object, name, value); - if (status != JSONSuccess) { - json_value_free(value); - } - return status; -} - -JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) { - JSON_Value *value = json_value_init_boolean(boolean); - JSON_Status status = json_object_set_value(object, name, value); - if (status != JSONSuccess) { - json_value_free(value); - } - return status; -} - -JSON_Status json_object_set_null(JSON_Object *object, const char *name) { - JSON_Value *value = json_value_init_null(); - JSON_Status status = json_object_set_value(object, name, value); - if (status != JSONSuccess) { - json_value_free(value); - } - return status; -} - -JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) { - const char *dot_pos = NULL; - JSON_Value *temp_value = NULL, *new_value = NULL; - JSON_Object *temp_object = NULL, *new_object = NULL; - JSON_Status status = JSONFailure; - size_t name_len = 0; - char *name_copy = NULL; - - if (object == NULL || name == NULL || value == NULL) { - return JSONFailure; - } - dot_pos = strchr(name, '.'); - if (dot_pos == NULL) { - return json_object_set_value(object, name, value); - } - name_len = dot_pos - name; - temp_value = json_object_getn_value(object, name, name_len); - if (temp_value) { - /* Don't overwrite existing non-object (unlike json_object_set_value, but it shouldn't be changed at this point) */ - if (json_value_get_type(temp_value) != JSONObject) { - return JSONFailure; - } - temp_object = json_value_get_object(temp_value); - return json_object_dotset_value(temp_object, dot_pos + 1, value); - } - new_value = json_value_init_object(); - if (new_value == NULL) { - return JSONFailure; - } - new_object = json_value_get_object(new_value); - status = json_object_dotset_value(new_object, dot_pos + 1, value); - if (status != JSONSuccess) { - json_value_free(new_value); - return JSONFailure; - } - name_copy = parson_strndup(name, name_len); - if (!name_copy) { - json_object_dotremove_internal(new_object, dot_pos + 1, 0); - json_value_free(new_value); - return JSONFailure; - } - status = json_object_add(object, name_copy, new_value); - if (status != JSONSuccess) { - parson_free(name_copy); - json_object_dotremove_internal(new_object, dot_pos + 1, 0); - json_value_free(new_value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string) { - JSON_Value *value = json_value_init_string(string); - if (value == NULL) { - return JSONFailure; - } - if (json_object_dotset_value(object, name, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_object_dotset_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len) { - JSON_Value *value = json_value_init_string_with_len(string, len); - if (value == NULL) { - return JSONFailure; - } - if (json_object_dotset_value(object, name, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number) { - JSON_Value *value = json_value_init_number(number); - if (value == NULL) { - return JSONFailure; - } - if (json_object_dotset_value(object, name, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean) { - JSON_Value *value = json_value_init_boolean(boolean); - if (value == NULL) { - return JSONFailure; - } - if (json_object_dotset_value(object, name, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_object_dotset_null(JSON_Object *object, const char *name) { - JSON_Value *value = json_value_init_null(); - if (value == NULL) { - return JSONFailure; - } - if (json_object_dotset_value(object, name, value) != JSONSuccess) { - json_value_free(value); - return JSONFailure; - } - return JSONSuccess; -} - -JSON_Status json_object_remove(JSON_Object *object, const char *name) { - return json_object_remove_internal(object, name, PARSON_TRUE); -} - -JSON_Status json_object_dotremove(JSON_Object *object, const char *name) { - return json_object_dotremove_internal(object, name, PARSON_TRUE); -} - -JSON_Status json_object_clear(JSON_Object *object) { - size_t i = 0; - if (object == NULL) { - return JSONFailure; - } - for (i = 0; i < json_object_get_count(object); i++) { - parson_free(object->names[i]); - json_value_free(object->values[i]); - } - object->count = 0; - return JSONSuccess; -} - -JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value) { - JSON_Value *temp_schema_value = NULL, *temp_value = NULL; - JSON_Array *schema_array = NULL, *value_array = NULL; - JSON_Object *schema_object = NULL, *value_object = NULL; - JSON_Value_Type schema_type = JSONError, value_type = JSONError; - const char *key = NULL; - size_t i = 0, count = 0; - if (schema == NULL || value == NULL) { - return JSONFailure; - } - schema_type = json_value_get_type(schema); - value_type = json_value_get_type(value); - if (schema_type != value_type && schema_type != JSONNull) { /* null represents all values */ - return JSONFailure; - } - switch (schema_type) { - case JSONArray: - schema_array = json_value_get_array(schema); - value_array = json_value_get_array(value); - count = json_array_get_count(schema_array); - if (count == 0) { - return JSONSuccess; /* Empty array allows all types */ - } - /* Get first value from array, rest is ignored */ - temp_schema_value = json_array_get_value(schema_array, 0); - for (i = 0; i < json_array_get_count(value_array); i++) { - temp_value = json_array_get_value(value_array, i); - if (json_validate(temp_schema_value, temp_value) != JSONSuccess) { - return JSONFailure; - } - } - return JSONSuccess; - case JSONObject: - schema_object = json_value_get_object(schema); - value_object = json_value_get_object(value); - count = json_object_get_count(schema_object); - if (count == 0) { - return JSONSuccess; /* Empty object allows all objects */ - } else if (json_object_get_count(value_object) < count) { - return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */ - } - for (i = 0; i < count; i++) { - key = json_object_get_name(schema_object, i); - temp_schema_value = json_object_get_value(schema_object, key); - temp_value = json_object_get_value(value_object, key); - if (temp_value == NULL) { - return JSONFailure; - } - if (json_validate(temp_schema_value, temp_value) != JSONSuccess) { - return JSONFailure; - } - } - return JSONSuccess; - case JSONString: - case JSONNumber: - case JSONBoolean: - case JSONNull: - return JSONSuccess; /* equality already tested before switch */ - case JSONError: - default: - return JSONFailure; - } -} - -int json_value_equals(const JSON_Value *a, const JSON_Value *b) { - JSON_Object *a_object = NULL, *b_object = NULL; - JSON_Array *a_array = NULL, *b_array = NULL; - const JSON_String *a_string = NULL, *b_string = NULL; - const char *key = NULL; - size_t a_count = 0, b_count = 0, i = 0; - JSON_Value_Type a_type, b_type; - a_type = json_value_get_type(a); - b_type = json_value_get_type(b); - if (a_type != b_type) { - return PARSON_FALSE; - } - switch (a_type) { - case JSONArray: - a_array = json_value_get_array(a); - b_array = json_value_get_array(b); - a_count = json_array_get_count(a_array); - b_count = json_array_get_count(b_array); - if (a_count != b_count) { - return PARSON_FALSE; - } - for (i = 0; i < a_count; i++) { - if (!json_value_equals(json_array_get_value(a_array, i), - json_array_get_value(b_array, i))) { - return PARSON_FALSE; - } - } - return PARSON_TRUE; - case JSONObject: - a_object = json_value_get_object(a); - b_object = json_value_get_object(b); - a_count = json_object_get_count(a_object); - b_count = json_object_get_count(b_object); - if (a_count != b_count) { - return PARSON_FALSE; - } - for (i = 0; i < a_count; i++) { - key = json_object_get_name(a_object, i); - if (!json_value_equals(json_object_get_value(a_object, key), - json_object_get_value(b_object, key))) { - return PARSON_FALSE; - } - } - return PARSON_TRUE; - case JSONString: - a_string = json_value_get_string_desc(a); - b_string = json_value_get_string_desc(b); - if (a_string == NULL || b_string == NULL) { - return PARSON_FALSE; /* shouldn't happen */ - } - return a_string->length == b_string->length && - memcmp(a_string->chars, b_string->chars, a_string->length) == 0; - case JSONBoolean: - return json_value_get_boolean(a) == json_value_get_boolean(b); - case JSONNumber: - return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */ - case JSONError: - return PARSON_TRUE; - case JSONNull: - return PARSON_TRUE; - default: - return PARSON_TRUE; - } -} - -JSON_Value_Type json_type(const JSON_Value *value) { - return json_value_get_type(value); -} - -JSON_Object *json_object(const JSON_Value *value) { - return json_value_get_object(value); -} - -JSON_Array *json_array(const JSON_Value *value) { - return json_value_get_array(value); -} - -const char *json_string(const JSON_Value *value) { - return json_value_get_string(value); -} - -size_t json_string_len(const JSON_Value *value) { - return json_value_get_string_len(value); -} - -double json_number(const JSON_Value *value) { - return json_value_get_number(value); -} - -int json_boolean(const JSON_Value *value) { - return json_value_get_boolean(value); -} - -void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) { - parson_malloc = malloc_fun; - parson_free = free_fun; -} - -void json_set_escape_slashes(int escape_slashes) { - parson_escape_slashes = escape_slashes; -} - -void json_set_float_serialization_format(const char *format) { - if (parson_float_format) { - parson_free(parson_float_format); - } - if (!format) { - parson_float_format = NULL; - return; - } - parson_float_format = parson_strdup(format); -} diff --git a/source/engine/parson.h b/source/engine/parson.h deleted file mode 100644 index 81bc110..0000000 --- a/source/engine/parson.h +++ /dev/null @@ -1,263 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Parson 1.4.0 (https://github.com/kgabis/parson) - Copyright (c) 2012 - 2022 Krzysztof Gabis - - 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. -*/ - -#ifndef parson_parson_h -#define parson_parson_h - -#ifdef __cplusplus -extern "C" { -#endif -#if 0 -} /* unconfuse xcode */ -#endif - -#define PARSON_VERSION_MAJOR 1 -#define PARSON_VERSION_MINOR 4 -#define PARSON_VERSION_PATCH 0 - -#define PARSON_VERSION_STRING "1.4.0" - -#include /* size_t */ - -/* Types and enums */ -typedef struct json_object_t JSON_Object; -typedef struct json_array_t JSON_Array; -typedef struct json_value_t JSON_Value; - -enum json_value_type { - JSONError = -1, - JSONNull = 1, - JSONString = 2, - JSONNumber = 3, - JSONObject = 4, - JSONArray = 5, - JSONBoolean = 6 -}; -typedef int JSON_Value_Type; - -enum json_result_t { - JSONSuccess = 0, - JSONFailure = -1 -}; -typedef int JSON_Status; - -typedef void *(*JSON_Malloc_Function)(size_t); -typedef void (*JSON_Free_Function)(void *); - -/* Call only once, before calling any other function from parson API. If not called, malloc and free - from stdlib will be used for all allocations */ -void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); - -/* Sets if slashes should be escaped or not when serializing JSON. By default slashes are escaped. - This function sets a global setting and is not thread safe. */ -void json_set_escape_slashes(int escape_slashes); - -/* Sets float format used for serialization of numbers. - Make sure it can't serialize to a string longer than PARSON_NUM_BUF_SIZE. - If format is null then the default format is used. */ -void json_set_float_serialization_format(const char *format); - -/* Parses first JSON value in a file, returns NULL in case of error */ -JSON_Value *json_parse_file(const char *filename); - -/* Parses first JSON value in a file and ignores comments (/ * * / and //), - returns NULL in case of error */ -JSON_Value *json_parse_file_with_comments(const char *filename); - -/* Parses first JSON value in a string, returns NULL in case of error */ -JSON_Value *json_parse_string(const char *string); - -/* Parses first JSON value in a string and ignores comments (/ * * / and //), - returns NULL in case of error */ -JSON_Value *json_parse_string_with_comments(const char *string); - -/* Serialization */ -size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */ -JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); -JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename); -char *json_serialize_to_string(const JSON_Value *value); - -/* Pretty serialization */ -size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */ -JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); -JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename); -char *json_serialize_to_string_pretty(const JSON_Value *value); - -void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */ - -/* Comparing */ -int json_value_equals(const JSON_Value *a, const JSON_Value *b); - -/* Validation - This is *NOT* JSON Schema. It validates json by checking if object have identically - named fields with matching types. - For example schema {"name":"", "age":0} will validate - {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, - but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. - In case of arrays, only first value in schema is checked against all values in tested array. - Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays, - null validates values of every type. - */ -JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value); - -/* - * JSON Object - */ -JSON_Value *json_object_get_value(const JSON_Object *object, const char *name); -const char *json_object_get_string(const JSON_Object *object, const char *name); -size_t json_object_get_string_len(const JSON_Object *object, const char *name); /* doesn't account for last null character */ -JSON_Object *json_object_get_object(const JSON_Object *object, const char *name); -JSON_Array *json_object_get_array(const JSON_Object *object, const char *name); -double json_object_get_number(const JSON_Object *object, const char *name); /* returns 0 on fail */ -int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ - -/* dotget functions enable addressing values with dot notation in nested objects, - just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). - Because valid names in JSON can contain dots, some values may be inaccessible - this way. */ -JSON_Value *json_object_dotget_value(const JSON_Object *object, const char *name); -const char *json_object_dotget_string(const JSON_Object *object, const char *name); -size_t json_object_dotget_string_len(const JSON_Object *object, const char *name); /* doesn't account for last null character */ -JSON_Object *json_object_dotget_object(const JSON_Object *object, const char *name); -JSON_Array *json_object_dotget_array(const JSON_Object *object, const char *name); -double json_object_dotget_number(const JSON_Object *object, const char *name); /* returns 0 on fail */ -int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ - -/* Functions to get available names */ -size_t json_object_get_count(const JSON_Object *object); -const char *json_object_get_name(const JSON_Object *object, size_t index); -JSON_Value *json_object_get_value_at(const JSON_Object *object, size_t index); -JSON_Value *json_object_get_wrapping_value(const JSON_Object *object); - -/* Functions to check if object has a value with a specific name. Returned value is 1 if object has - * a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */ -int json_object_has_value(const JSON_Object *object, const char *name); -int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type); - -int json_object_dothas_value(const JSON_Object *object, const char *name); -int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type); - -/* Creates new name-value pair or frees and replaces old value with a new one. - * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */ -JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value); -JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string); -JSON_Status json_object_set_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len); /* length shouldn't include last null character */ -JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number); -JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean); -JSON_Status json_object_set_null(JSON_Object *object, const char *name); - -/* Works like dotget functions, but creates whole hierarchy if necessary. - * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */ -JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value); -JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string); -JSON_Status json_object_dotset_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len); /* length shouldn't include last null character */ -JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number); -JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean); -JSON_Status json_object_dotset_null(JSON_Object *object, const char *name); - -/* Frees and removes name-value pair */ -JSON_Status json_object_remove(JSON_Object *object, const char *name); - -/* Works like dotget function, but removes name-value pair only on exact match. */ -JSON_Status json_object_dotremove(JSON_Object *object, const char *key); - -/* Removes all name-value pairs in object */ -JSON_Status json_object_clear(JSON_Object *object); - -/* - *JSON Array - */ -JSON_Value *json_array_get_value(const JSON_Array *array, size_t index); -const char *json_array_get_string(const JSON_Array *array, size_t index); -size_t json_array_get_string_len(const JSON_Array *array, size_t index); /* doesn't account for last null character */ -JSON_Object *json_array_get_object(const JSON_Array *array, size_t index); -JSON_Array *json_array_get_array(const JSON_Array *array, size_t index); -double json_array_get_number(const JSON_Array *array, size_t index); /* returns 0 on fail */ -int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ -size_t json_array_get_count(const JSON_Array *array); -JSON_Value *json_array_get_wrapping_value(const JSON_Array *array); - -/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. - * Order of values in array may change during execution. */ -JSON_Status json_array_remove(JSON_Array *array, size_t i); - -/* Frees and removes from array value at given index and replaces it with given one. - * Does nothing and returns JSONFailure if index doesn't exist. - * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */ -JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value); -JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char *string); -JSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i, const char *string, size_t len); /* length shouldn't include last null character */ -JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number); -JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean); -JSON_Status json_array_replace_null(JSON_Array *array, size_t i); - -/* Frees and removes all values from array */ -JSON_Status json_array_clear(JSON_Array *array); - -/* Appends new value at the end of array. - * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */ -JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value); -JSON_Status json_array_append_string(JSON_Array *array, const char *string); -JSON_Status json_array_append_string_with_len(JSON_Array *array, const char *string, size_t len); /* length shouldn't include last null character */ -JSON_Status json_array_append_number(JSON_Array *array, double number); -JSON_Status json_array_append_boolean(JSON_Array *array, int boolean); -JSON_Status json_array_append_null(JSON_Array *array); - -/* - *JSON Value - */ -JSON_Value *json_value_init_object(void); -JSON_Value *json_value_init_array(void); -JSON_Value *json_value_init_string(const char *string); /* copies passed string */ -JSON_Value *json_value_init_string_with_len(const char *string, size_t length); /* copies passed string, length shouldn't include last null character */ -JSON_Value *json_value_init_number(double number); -JSON_Value *json_value_init_boolean(int boolean); -JSON_Value *json_value_init_null(void); -JSON_Value *json_value_deep_copy(const JSON_Value *value); -void json_value_free(JSON_Value *value); - -JSON_Value_Type json_value_get_type(const JSON_Value *value); -JSON_Object *json_value_get_object(const JSON_Value *value); -JSON_Array *json_value_get_array(const JSON_Value *value); -const char *json_value_get_string(const JSON_Value *value); -size_t json_value_get_string_len(const JSON_Value *value); /* doesn't account for last null character */ -double json_value_get_number(const JSON_Value *value); -int json_value_get_boolean(const JSON_Value *value); -JSON_Value *json_value_get_parent(const JSON_Value *value); - -/* Same as above, but shorter */ -JSON_Value_Type json_type(const JSON_Value *value); -JSON_Object *json_object(const JSON_Value *value); -JSON_Array *json_array(const JSON_Value *value); -const char *json_string(const JSON_Value *value); -size_t json_string_len(const JSON_Value *value); /* doesn't account for last null character */ -double json_number(const JSON_Value *value); -int json_boolean(const JSON_Value *value); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/engine/particle.h b/source/engine/particle.h index a1d8731..1fbae39 100644 --- a/source/engine/particle.h +++ b/source/engine/particle.h @@ -2,6 +2,7 @@ #define PARTICLE_H #include "HandmadeMath.h" +#include "transform.h" struct particle { HMM_Vec3 pos; @@ -15,12 +16,13 @@ struct particle { }; }; -struct emitter { +typedef struct emitter { struct particle *particles; + transform3d t; int max; double life; void (*seeder)(struct particle *p); /* Called to initialize each particle */ -}; +} emitter; struct emitter make_emitter(); void free_emitter(struct emitter e); diff --git a/source/engine/render.c b/source/engine/render.c index 8201886..88c58dc 100644 --- a/source/engine/render.c +++ b/source/engine/render.c @@ -506,7 +506,7 @@ void full_2d_pass(struct window *window) //////////// 2D projection cpVect pos = cam_pos(); - projection = HMM_Orthographic_LH_ZO( + projection = HMM_Orthographic_LH_NO( pos.x - zoom * window->rwidth / 2, pos.x + zoom * window->rwidth / 2, pos.y - zoom * window->rheight / 2, diff --git a/source/engine/sound/dsp.c b/source/engine/sound/dsp.c index 284170a..77bc693 100644 --- a/source/engine/sound/dsp.c +++ b/source/engine/sound/dsp.c @@ -435,7 +435,6 @@ dsp_node *dsp_fwd_delay(double sec, double decay) d->ring = NULL; d->ring = ringnew(d->ring, sec*CHANNELS*SAMPLERATE*2); ringheader(d->ring)->write += CHANNELS*SAMPLERATE*sec; - YughWarn("FWD DELAY"); return make_node(d, filter_fwd_delay, delay_free); } @@ -448,47 +447,40 @@ double tau2pole(double tau) void dsp_adsr_fillbuf(struct dsp_adsr *adsr, soundbyte *out, int n) { - soundbyte val; + soundbyte val; - for (int i = 0; i < n; i++) { - if (adsr->time > adsr->rls) { - // Totally decayed - adsr->out = 0.f; + for (int i = 0; i < n; i++) { + if (adsr->time > adsr->rls) { + adsr->out = 0.f; + goto fin; + } - goto fin; - } + if (adsr->time > adsr->sus) { + // Release phase + adsr->out = adsr->rls_t * adsr->out; + goto fin; + } - if (adsr->time > adsr->sus) { - // Release phase - adsr->out = adsr->rls_t * adsr->out; + if (adsr->time > adsr->dec) { + // Sustain phase + adsr->out = adsr->sus_pwr; + goto fin; + } - goto fin; - } + if (adsr->time > adsr->atk) { + // Decay phase + adsr->out = (1 - adsr->dec_t) * adsr->sus_pwr + adsr->dec_t * adsr->out; + goto fin; + } - if (adsr->time > adsr->dec) { - // Sustain phase - adsr->out = adsr->sus_pwr; + // Attack phase + adsr->out = (1-adsr->atk_t) + adsr->atk_t * adsr->out; + + fin: - goto fin; - } - - if (adsr->time > adsr->atk) { - // Decay phase - adsr->out = (1 - adsr->dec_t) * adsr->sus_pwr + adsr->dec_t * adsr->out; - - goto fin; - } - - // Attack phase - adsr->out = (1-adsr->atk_t) + adsr->atk_t * adsr->out; - - - - fin: - - val = SHRT_MAX * adsr->out; - out[i*CHANNELS] = out[i*CHANNELS+1] = val; - adsr->time += (double)(1000.f / SAMPLERATE); + val = SHRT_MAX * adsr->out; + out[i*CHANNELS] = out[i*CHANNELS+1] = val; + adsr->time += (double)(1000.f / SAMPLERATE); } } diff --git a/source/engine/sprite.c b/source/engine/sprite.c index 797035b..dbe31ac 100644 --- a/source/engine/sprite.c +++ b/source/engine/sprite.c @@ -62,7 +62,6 @@ int make_sprite(int go) { .layer = 0, .next = -1, .enabled = 1}; - int id; freelist_grab(id, sprites); sprites[id] = sprite; @@ -119,21 +118,24 @@ int sprite_sort(int *a, int *b) void sprite_draw_all() { sg_apply_pipeline(pip_sprite); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(projection)); - static int *layers; + int *layers = NULL; if (layers) arrfree(layers); for (int i = 0; i < freelist_len(sprites); i++) - if (sprites[i].next == -1 && sprites[i].go >= 0 && sprites[i].enabled) + if (sprites[i].next == -1 && sprites[i].go >= 0 && sprites[i].enabled) arrpush(layers, i); - if (arrlen(layers) == 0) return; + if (!layers || arrlen(layers) == 0) return; if (arrlen(layers) > 1) qsort(layers, arrlen(layers), sizeof(*layers), sprite_sort); for (int i = 0; i < arrlen(layers); i++) sprite_draw(&sprites[layers[i]]); + + arrfree(layers); } + void sprite_loadtex(struct sprite *sprite, const char *path, struct glrect frame) { if (!sprite) { YughWarn("NO SPRITE!"); @@ -201,20 +203,16 @@ void sprite_initialize() { }); } -/* offset given in texture offset, so -0.5,-0.5 results in it being centered */ void tex_draw(struct Texture *tex, HMM_Mat3 m, struct glrect r, struct rgba color, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba emissive) { struct sprite_vert verts[4]; + float w = tex->width*st_s_w(r); + float h = tex->height*st_s_h(r); HMM_Vec2 sposes[4] = { {0.0,0.0}, - {1.0,0.0}, - {0.0,1.0}, - {1.0,1.0}, - }; - - HMM_Vec2 t_scale = { - tex->width * st_s_w(r), //*size.X; - tex->height * st_s_h(r) // * size.Y + {w,0.0}, + {0.0,h}, + {w,h} }; for (int i = 0; i < 4; i++) { @@ -222,25 +220,15 @@ void tex_draw(struct Texture *tex, HMM_Mat3 m, struct glrect r, struct rgba colo verts[i].color = color; verts[i].emissive = emissive; } - - if (!wrap) { - verts[0].uv.X = r.s0; - verts[0].uv.Y = r.t1; - verts[1].uv.X = r.s1; - verts[1].uv.Y = r.t1; - verts[2].uv.X = r.s0; - verts[2].uv.Y = r.t0; - verts[3].uv.X = r.s1; - verts[3].uv.Y = r.t0; - } else { -// verts[0].uv = HMM_MulV2((HMM_Vec2){0,0}, size); -// verts[1].uv = HMM_MulV2((HMM_Vec2){1,0}, size); -// verts[2].uv = HMM_MulV2((HMM_Vec2){0,1}, size); -// verts[3].uv = HMM_MulV2((HMM_Vec2){1,1}, size); - for (int i = 0; i < 4; i++) - verts[i].uv = HMM_AddV2(verts[i].uv, wrapoffset); - } + verts[0].uv.X = r.s0; + verts[0].uv.Y = r.t1; + verts[1].uv.X = r.s1; + verts[1].uv.Y = r.t1; + verts[2].uv.X = r.s0; + verts[2].uv.Y = r.t0; + verts[3].uv.X = r.s1; + verts[3].uv.Y = r.t0; bind_sprite.fs.images[0] = tex->id; @@ -256,16 +244,9 @@ void sprite_draw(struct sprite *sprite) { if (sprite->tex) { HMM_Mat3 m = t_go2world(go); - - HMM_Mat3 ss = HMM_M3D(1); - ss.Columns[0].X = sprite->t.scale.X * sprite->tex->width * st_s_w(sprite->frame); - ss.Columns[1].Y = sprite->t.scale.Y * sprite->tex->height * st_s_h(sprite->frame); - HMM_Mat3 ts = HMM_M3D(1); - ts.Columns[2] = (HMM_Vec3){sprite->t.pos.X, sprite->t.pos.Y, 1}; - - HMM_Mat3 sm = HMM_MulM3(ss, ts); - m = HMM_MulM3(m, sm); - tex_draw(sprite->tex, m, sprite->frame, sprite->color, 0, (HMM_Vec2){0,0}, 0, sprite->emissive); + HMM_Mat3 sm = transform2d2mat(sprite->t); + + tex_draw(sprite->tex, HMM_MulM3(m, sm), sprite->frame, sprite->color, 0, (HMM_Vec2){0,0}, 0, sprite->emissive); } } diff --git a/source/engine/tinyspline.c b/source/engine/tinyspline.c deleted file mode 100644 index f19c6df..0000000 --- a/source/engine/tinyspline.c +++ /dev/null @@ -1,2932 +0,0 @@ -#define TINYSPLINE_EXPORT -#include "tinyspline.h" -#include "parson.h" /* serialization */ - -#include /* fabs, sqrt, acos */ -#include /* varargs */ -#include /* FILE, fopen */ -#include /* malloc, free */ -#include /* memcpy, memmove */ - -/* Suppress some useless MSVC warnings. */ -#ifdef _MSC_VER -#pragma warning(push) -/* address of dllimport */ -#pragma warning(disable : 4232) -/* function not inlined */ -#pragma warning(disable : 4710) -/* byte padding */ -#pragma warning(disable : 4820) -/* meaningless deprecation */ -#pragma warning(disable : 4996) -/* Spectre mitigation */ -#pragma warning(disable : 5045) -#endif - -#define INIT_OUT_BSPLINE(in, out) \ - if ((in) != (out)) \ - ts_int_bspline_init(out); - -/*! @name Internal Structs and Functions - * - * Internal functions are prefixed with \e ts_int (int for internal). - * - * @{ - */ -/** - * Stores the private data of ::tsBSpline. - */ -struct tsBSplineImpl { - size_t deg; /**< Degree of B-Spline basis function. */ - size_t dim; /**< Dimensionality of the control points (2D => x, y). */ - size_t n_ctrlp; /**< Number of control points. */ - size_t n_knots; /**< Number of knots (n_ctrlp + deg + 1). */ -}; - -/** - * Stores the private data of ::tsDeBoorNet. - */ -struct tsDeBoorNetImpl { - tsReal u; /**< The evaluated knot. */ - size_t k; /**< The index [u_k, u_k+1) */ - size_t s; /**< Multiplicity of u_k. */ - size_t h; /**< Number of insertions required to obtain result. */ - size_t dim; /**< Dimensionality of the points (2D => x, y). */ - size_t n_points; /** Number of points in `points'. */ -}; - -void ts_int_bspline_init(tsBSpline *spline) { - spline->pImpl = NULL; -} - -size_t -ts_int_bspline_sof_state(const tsBSpline *spline) { - return sizeof(struct tsBSplineImpl) + - ts_bspline_sof_control_points(spline) + - ts_bspline_sof_knots(spline); -} - -tsReal * -ts_int_bspline_access_ctrlp(const tsBSpline *spline) { - return (tsReal *)(&spline->pImpl[1]); -} - -tsReal * -ts_int_bspline_access_knots(const tsBSpline *spline) { - return ts_int_bspline_access_ctrlp(spline) + - ts_bspline_len_control_points(spline); -} - -tsError -ts_int_bspline_access_ctrlp_at(const tsBSpline *spline, - size_t index, - tsReal **ctrlp, - tsStatus *status) { - const size_t num = ts_bspline_num_control_points(spline); - if (index >= num) { - TS_RETURN_2(status, TS_INDEX_ERROR, - "index (%lu) >= num(control_points) (%lu)", - (unsigned long)index, - (unsigned long)num) - } - *ctrlp = ts_int_bspline_access_ctrlp(spline) + - index * ts_bspline_dimension(spline); - TS_RETURN_SUCCESS(status) -} - -tsError -ts_int_bspline_access_knot_at(const tsBSpline *spline, - size_t index, - tsReal *knot, - tsStatus *status) { - const size_t num = ts_bspline_num_knots(spline); - if (index >= num) { - TS_RETURN_2(status, TS_INDEX_ERROR, - "index (%lu) >= num(knots) (%lu)", - (unsigned long)index, - (unsigned long)num) - } - *knot = ts_int_bspline_access_knots(spline)[index]; - TS_RETURN_SUCCESS(status) -} - -void ts_int_deboornet_init(tsDeBoorNet *net) { - net->pImpl = NULL; -} - -size_t -ts_int_deboornet_sof_state(const tsDeBoorNet *net) { - return sizeof(struct tsDeBoorNetImpl) + - ts_deboornet_sof_points(net) + - ts_deboornet_sof_result(net); -} - -tsReal * -ts_int_deboornet_access_points(const tsDeBoorNet *net) { - return (tsReal *)(&net->pImpl[1]); -} - -tsReal * -ts_int_deboornet_access_result(const tsDeBoorNet *net) { - if (ts_deboornet_num_result(net) == 2) { - return ts_int_deboornet_access_points(net); - } else { - return ts_int_deboornet_access_points(net) + - /* Last point in `points`. */ - (ts_deboornet_len_points(net) - - ts_deboornet_dimension(net)); - } -} -/*! @} */ - -/*! @name B-Spline Data - * - * @{ - */ -size_t -ts_bspline_degree(const tsBSpline *spline) { - return spline->pImpl->deg; -} - -size_t -ts_bspline_order(const tsBSpline *spline) { - return ts_bspline_degree(spline) + 1; -} - -size_t -ts_bspline_dimension(const tsBSpline *spline) { - return spline->pImpl->dim; -} - -size_t -ts_bspline_len_control_points(const tsBSpline *spline) { - return ts_bspline_num_control_points(spline) * - ts_bspline_dimension(spline); -} - -size_t -ts_bspline_num_control_points(const tsBSpline *spline) { - return spline->pImpl->n_ctrlp; -} - -size_t -ts_bspline_sof_control_points(const tsBSpline *spline) { - return ts_bspline_len_control_points(spline) * sizeof(tsReal); -} - -const tsReal * -ts_bspline_control_points_ptr(const tsBSpline *spline) { - return ts_int_bspline_access_ctrlp(spline); -} - -tsError -ts_bspline_control_points(const tsBSpline *spline, - tsReal **ctrlp, - tsStatus *status) { - const size_t size = ts_bspline_sof_control_points(spline); - *ctrlp = (tsReal *)malloc(size); - if (!*ctrlp) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - memcpy(*ctrlp, ts_int_bspline_access_ctrlp(spline), size); - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_control_point_at_ptr(const tsBSpline *spline, - size_t index, - const tsReal **ctrlp, - tsStatus *status) { - tsReal *vals; - tsError err; - TS_TRY(try, err, status) - TS_CALL(try, err, ts_int_bspline_access_ctrlp_at(spline, index, &vals, status)) - *ctrlp = vals; - TS_CATCH(err) - *ctrlp = NULL; - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_set_control_points(tsBSpline *spline, - const tsReal *ctrlp, - tsStatus *status) { - const size_t size = ts_bspline_sof_control_points(spline); - memmove(ts_int_bspline_access_ctrlp(spline), ctrlp, size); - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_set_control_point_at(tsBSpline *spline, - size_t index, - const tsReal *ctrlp, - tsStatus *status) { - tsReal *to; - size_t size; - tsError err; - TS_TRY(try, err, status) - TS_CALL(try, err, ts_int_bspline_access_ctrlp_at(spline, index, &to, status)) - size = ts_bspline_dimension(spline) * sizeof(tsReal); - memcpy(to, ctrlp, size); - TS_END_TRY_RETURN(err) -} - -size_t -ts_bspline_num_knots(const tsBSpline *spline) { - return spline->pImpl->n_knots; -} - -size_t -ts_bspline_sof_knots(const tsBSpline *spline) { - return ts_bspline_num_knots(spline) * sizeof(tsReal); -} - -const tsReal * -ts_bspline_knots_ptr(const tsBSpline *spline) { - return ts_int_bspline_access_knots(spline); -} - -tsError -ts_bspline_knots(const tsBSpline *spline, - tsReal **knots, - tsStatus *status) { - const size_t size = ts_bspline_sof_knots(spline); - *knots = (tsReal *)malloc(size); - if (!*knots) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - memcpy(*knots, ts_int_bspline_access_knots(spline), size); - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_knot_at(const tsBSpline *spline, - size_t index, - tsReal *knot, - tsStatus *status) { - return ts_int_bspline_access_knot_at(spline, index, knot, status); -} - -tsError -ts_bspline_set_knots(tsBSpline *spline, - const tsReal *knots, - tsStatus *status) { - const size_t size = ts_bspline_sof_knots(spline); - const size_t num_knots = ts_bspline_num_knots(spline); - const size_t order = ts_bspline_order(spline); - size_t idx, mult; - tsReal lst_knot, knot; - lst_knot = knots[0]; - mult = 1; - for (idx = 1; idx < num_knots; idx++) { - knot = knots[idx]; - if (ts_knots_equal(lst_knot, knot)) { - mult++; - } else if (lst_knot > knot) { - TS_RETURN_1(status, TS_KNOTS_DECR, - "decreasing knot vector at index: %lu", - (unsigned long)idx) - } else { - mult = 0; - } - if (mult > order) { - TS_RETURN_3(status, TS_MULTIPLICITY, - "mult(%f) (%lu) > order (%lu)", - knot, (unsigned long)mult, - (unsigned long)order) - } - lst_knot = knot; - } - memmove(ts_int_bspline_access_knots(spline), knots, size); - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_set_knots_varargs(tsBSpline *spline, - tsStatus *status, - tsReal knot0, - double knot1, - ...) { - tsReal *values = NULL; - va_list argp; - size_t idx; - tsError err; - - TS_TRY(try, err, status) - TS_CALL(try, err, ts_bspline_knots(spline, &values, status)) - - values[0] = knot0; - values[1] = (tsReal)knot1; - va_start(argp, knot1); - for (idx = 2; idx < ts_bspline_num_knots(spline); idx++) - values[idx] = (tsReal)va_arg(argp, double); - va_end(argp); - - TS_CALL(try, err, ts_bspline_set_knots(spline, values, status)) - TS_FINALLY - if (values) - free(values); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_set_knot_at(tsBSpline *spline, - size_t index, - tsReal knot, - tsStatus *status) { - tsError err; - tsReal *knots = NULL; - /* This is only for initialization. */ - tsReal oldKnot = ts_int_bspline_access_knots(spline)[0]; - TS_TRY(try, err, status) - TS_CALL(try, err, ts_int_bspline_access_knot_at(spline, index, &oldKnot, status)) - /* knots must be set after reading oldKnot because the catch - * block assumes that oldKnot contains the correct value if - * knots is not NULL. */ - knots = ts_int_bspline_access_knots(spline); - knots[index] = knot; - TS_CALL(try, err, ts_bspline_set_knots(spline, knots, status)) - TS_CATCH(err) - /* If knots is not NULL, oldKnot contains the correct value. */ - if (knots) - knots[index] = oldKnot; - TS_END_TRY_RETURN(err) -} -/*! @} */ - -/*! @name B-Spline Initialization - * - * @{ - */ -tsBSpline -ts_bspline_init(void) { - tsBSpline spline; - ts_int_bspline_init(&spline); - return spline; -} - -tsError -ts_int_bspline_generate_knots(const tsBSpline *spline, - tsBSplineType type, - tsStatus *status) { - const size_t n_knots = ts_bspline_num_knots(spline); - const size_t deg = ts_bspline_degree(spline); - const size_t order = ts_bspline_order(spline); - tsReal fac; /**< Factor used to calculate the knot values. */ - size_t i; /**< Used in for loops. */ - tsReal *knots; /**< Pointer to the knots of \p _result_. */ - - /* order >= 1 implies 2*order >= 2 implies n_knots >= 2 */ - if (type == TS_BEZIERS && n_knots % order != 0) { - TS_RETURN_2(status, TS_NUM_KNOTS, - "num(knots) (%lu) %% order (%lu) != 0", - (unsigned long)n_knots, (unsigned long)order) - } - - knots = ts_int_bspline_access_knots(spline); - - if (type == TS_OPENED) { - knots[0] = TS_DOMAIN_DEFAULT_MIN; /* n_knots >= 2 */ - fac = (TS_DOMAIN_DEFAULT_MAX - TS_DOMAIN_DEFAULT_MIN) / (n_knots - 1); /* n_knots >= 2 */ - for (i = 1; i < n_knots - 1; i++) - knots[i] = TS_DOMAIN_DEFAULT_MIN + i * fac; - knots[i] = TS_DOMAIN_DEFAULT_MAX; /* n_knots >= 2 */ - } else if (type == TS_CLAMPED) { - /* n_knots >= 2*order == 2*(deg+1) == 2*deg + 2 > 2*deg - 1 */ - fac = (TS_DOMAIN_DEFAULT_MAX - TS_DOMAIN_DEFAULT_MIN) / (n_knots - 2 * deg - 1); - ts_arr_fill(knots, order, TS_DOMAIN_DEFAULT_MIN); - for (i = order; i < n_knots - order; i++) - knots[i] = TS_DOMAIN_DEFAULT_MIN + (i - deg) * fac; - ts_arr_fill(knots + i, order, TS_DOMAIN_DEFAULT_MAX); - } else if (type == TS_BEZIERS) { - /* n_knots >= 2*order implies n_knots/order >= 2 */ - fac = (TS_DOMAIN_DEFAULT_MAX - TS_DOMAIN_DEFAULT_MIN) / (n_knots / order - 1); - ts_arr_fill(knots, order, TS_DOMAIN_DEFAULT_MIN); - for (i = order; i < n_knots - order; i += order) - ts_arr_fill(knots + i, - order, - TS_DOMAIN_DEFAULT_MIN + (i / order) * fac); - ts_arr_fill(knots + i, order, TS_DOMAIN_DEFAULT_MAX); - } - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_new(size_t num_control_points, - size_t dimension, - size_t degree, - tsBSplineType type, - tsBSpline *spline, - tsStatus *status) { - const size_t order = degree + 1; - const size_t num_knots = num_control_points + order; - const size_t len_ctrlp = num_control_points * dimension; - - const size_t sof_real = sizeof(tsReal); - const size_t sof_impl = sizeof(struct tsBSplineImpl); - const size_t sof_ctrlp_vec = len_ctrlp * sof_real; - const size_t sof_knots_vec = num_knots * sof_real; - const size_t sof_spline = sof_impl + sof_ctrlp_vec + sof_knots_vec; - tsError err; - - ts_int_bspline_init(spline); - - if (dimension < 1) { - TS_RETURN_0(status, TS_DIM_ZERO, "unsupported dimension: 0") - } - if (num_knots > TS_MAX_NUM_KNOTS) { - TS_RETURN_2(status, TS_NUM_KNOTS, - "unsupported number of knots: %lu > %i", - (unsigned long)num_knots, TS_MAX_NUM_KNOTS) - } - if (degree >= num_control_points) { - TS_RETURN_2(status, TS_DEG_GE_NCTRLP, - "degree (%lu) >= num(control_points) (%lu)", - (unsigned long)degree, - (unsigned long)num_control_points) - } - - spline->pImpl = (struct tsBSplineImpl *)malloc(sof_spline); - if (!spline->pImpl) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - - spline->pImpl->deg = degree; - spline->pImpl->dim = dimension; - spline->pImpl->n_ctrlp = num_control_points; - spline->pImpl->n_knots = num_knots; - - TS_TRY(try, err, status) - TS_CALL(try, err, ts_int_bspline_generate_knots(spline, type, status)) - TS_CATCH(err) - ts_bspline_free(spline); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_new_with_control_points(size_t num_control_points, - size_t dimension, - size_t degree, - tsBSplineType type, - tsBSpline *spline, - tsStatus *status, - double first, - ...) { - tsReal *ctrlp = NULL; - va_list argp; - size_t i; - tsError err; - - TS_TRY(try, err, status) - TS_CALL(try, err, ts_bspline_new(num_control_points, dimension, degree, type, spline, status)) - TS_CATCH(err) - ts_bspline_free(spline); - TS_END_TRY_ROE(err) - ctrlp = ts_int_bspline_access_ctrlp(spline); - - ctrlp[0] = (tsReal)first; - va_start(argp, first); - for (i = 1; i < ts_bspline_len_control_points(spline); i++) - ctrlp[i] = (tsReal)va_arg(argp, double); - va_end(argp); - - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_copy(const tsBSpline *src, - tsBSpline *dest, - tsStatus *status) { - size_t size; - if (src == dest) - TS_RETURN_SUCCESS(status) - ts_int_bspline_init(dest); - size = ts_int_bspline_sof_state(src); - dest->pImpl = (struct tsBSplineImpl *)malloc(size); - if (!dest->pImpl) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - memcpy(dest->pImpl, src->pImpl, size); - TS_RETURN_SUCCESS(status) -} - -void ts_bspline_move(tsBSpline *src, - tsBSpline *dest) { - if (src == dest) - return; - dest->pImpl = src->pImpl; - ts_int_bspline_init(src); -} - -void ts_bspline_free(tsBSpline *spline) { - if (spline->pImpl) - free(spline->pImpl); - ts_int_bspline_init(spline); -} -/*! @} */ - -/*! @name De Boor Net Data - * - * @{ - */ -tsReal -ts_deboornet_knot(const tsDeBoorNet *net) { - return net->pImpl->u; -} - -size_t -ts_deboornet_index(const tsDeBoorNet *net) { - return net->pImpl->k; -} - -size_t -ts_deboornet_multiplicity(const tsDeBoorNet *net) { - return net->pImpl->s; -} - -size_t -ts_deboornet_num_insertions(const tsDeBoorNet *net) { - return net->pImpl->h; -} - -size_t -ts_deboornet_dimension(const tsDeBoorNet *net) { - return net->pImpl->dim; -} - -size_t -ts_deboornet_len_points(const tsDeBoorNet *net) { - return ts_deboornet_num_points(net) * - ts_deboornet_dimension(net); -} - -size_t -ts_deboornet_num_points(const tsDeBoorNet *net) { - return net->pImpl->n_points; -} - -size_t -ts_deboornet_sof_points(const tsDeBoorNet *net) { - return ts_deboornet_len_points(net) * sizeof(tsReal); -} - -const tsReal * -ts_deboornet_points_ptr(const tsDeBoorNet *net) { - return ts_int_deboornet_access_points(net); -} - -tsError -ts_deboornet_points(const tsDeBoorNet *net, - tsReal **points, - tsStatus *status) { - const size_t size = ts_deboornet_sof_points(net); - *points = (tsReal *)malloc(size); - if (!*points) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - memcpy(*points, ts_int_deboornet_access_points(net), size); - TS_RETURN_SUCCESS(status) -} - -size_t -ts_deboornet_len_result(const tsDeBoorNet *net) { - return ts_deboornet_num_result(net) * - ts_deboornet_dimension(net); -} - -size_t -ts_deboornet_num_result(const tsDeBoorNet *net) { - return ts_deboornet_num_points(net) == 2 ? 2 : 1; -} - -size_t -ts_deboornet_sof_result(const tsDeBoorNet *net) { - return ts_deboornet_len_result(net) * sizeof(tsReal); -} - -const tsReal * -ts_deboornet_result_ptr(const tsDeBoorNet *net) { - return ts_int_deboornet_access_result(net); -} - -tsError -ts_deboornet_result(const tsDeBoorNet *net, - tsReal **result, - tsStatus *status) { - const size_t size = ts_deboornet_sof_result(net); - *result = (tsReal *)malloc(size); - if (!*result) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - memcpy(*result, ts_int_deboornet_access_result(net), size); - TS_RETURN_SUCCESS(status) -} -/*! @} */ - -/*! @name De Boor Net Initialization - * - * @{ - */ -tsDeBoorNet -ts_deboornet_init(void) { - tsDeBoorNet net; - ts_int_deboornet_init(&net); - return net; -} - -tsError -ts_int_deboornet_new(const tsBSpline *spline, - tsDeBoorNet *net, - tsStatus *status) { - const size_t dim = ts_bspline_dimension(spline); - const size_t deg = ts_bspline_degree(spline); - const size_t order = ts_bspline_order(spline); - const size_t num_points = (size_t)(order * (order + 1) * 0.5f); - /* Handle `order == 1' which generates too few points. */ - const size_t fixed_num_points = num_points < 2 ? 2 : num_points; - - const size_t sof_real = sizeof(tsReal); - const size_t sof_impl = sizeof(struct tsDeBoorNetImpl); - const size_t sof_points_vec = fixed_num_points * dim * sof_real; - const size_t sof_net = sof_impl * sof_points_vec; - - net->pImpl = (struct tsDeBoorNetImpl *)malloc(sof_net); - if (!net->pImpl) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - - net->pImpl->u = 0.f; - net->pImpl->k = 0; - net->pImpl->s = 0; - net->pImpl->h = deg; - net->pImpl->dim = dim; - net->pImpl->n_points = fixed_num_points; - TS_RETURN_SUCCESS(status) -} - -void ts_deboornet_free(tsDeBoorNet *net) { - if (net->pImpl) - free(net->pImpl); - ts_int_deboornet_init(net); -} - -tsError -ts_deboornet_copy(const tsDeBoorNet *src, - tsDeBoorNet *dest, - tsStatus *status) { - size_t size; - if (src == dest) - TS_RETURN_SUCCESS(status) - ts_int_deboornet_init(dest); - size = ts_int_deboornet_sof_state(src); - dest->pImpl = (struct tsDeBoorNetImpl *)malloc(size); - if (!dest->pImpl) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - memcpy(dest->pImpl, src->pImpl, size); - TS_RETURN_SUCCESS(status) -} - -void ts_deboornet_move(tsDeBoorNet *src, - tsDeBoorNet *dest) { - if (src == dest) - return; - dest->pImpl = src->pImpl; - ts_int_deboornet_init(src); -} -/*! @} */ - -/*! @name Interpolation and Approximation Functions - * - * @{ - */ -tsError -ts_int_cubic_point(const tsReal *point, - size_t dim, - tsBSpline *spline, - tsStatus *status) { - const size_t size = dim * sizeof(tsReal); - tsReal *ctrlp = NULL; - size_t i; - tsError err; - TS_CALL_ROE(err, ts_bspline_new( - 4, dim, 3, - TS_CLAMPED, spline, status)) - ctrlp = ts_int_bspline_access_ctrlp(spline); - for (i = 0; i < 4; i++) { - memcpy(ctrlp + i * dim, - point, - size); - } - TS_RETURN_SUCCESS(status) -} - -tsError -ts_int_thomas_algorithm(const tsReal *a, - const tsReal *b, - const tsReal *c, - size_t num, - size_t dim, - tsReal *d, - tsStatus *status) { - size_t i, j, k, l; - tsReal m, *cc = NULL; - tsError err; - - if (dim == 0) { - TS_RETURN_0(status, TS_DIM_ZERO, - "unsupported dimension: 0") - } - if (num <= 1) { - TS_RETURN_1(status, TS_NUM_POINTS, - "num(points) (%lu) <= 1", - (unsigned long)num) - } - cc = (tsReal *)malloc(num * sizeof(tsReal)); - if (!cc) TS_RETURN_0(status, TS_MALLOC, "out of memory") - - TS_TRY(try, err, status) - /* Forward sweep. */ - if (fabs(b[0]) <= fabs(c[0])) { - TS_THROW_2(try, err, status, TS_NO_RESULT, - "error: |%f| <= |%f|", b[0], c[0]) - } - /* |b[i]| > |c[i]| implies that |b[i]| > 0. Thus, the following - * statements cannot evaluate to division by zero.*/ - cc[0] = c[0] / b[0]; - for (i = 0; i < dim; i++) - d[i] = d[i] / b[0]; - for (i = 1; i < num; i++) { - if (fabs(b[i]) <= fabs(a[i]) + fabs(c[i])) { - TS_THROW_3(try, err, status, TS_NO_RESULT, - "error: |%f| <= |%f| + |%f|", - b[i], a[i], c[i]) - } - /* |a[i]| < |b[i]| and cc[i - 1] < 1. Therefore, the - * following statement cannot evaluate to division by - * zero. */ - m = 1.f / (b[i] - a[i] * cc[i - 1]); - /* |b[i]| > |a[i]| + |c[i]| implies that there must be - * an eps > 0 such that |b[i]| = |a[i]| + |c[i]| + eps. - * Even if |a[i]| is 0 (by which the result of the - * following statement becomes maximum), |c[i]| is less - * than |b[i]| by an amount of eps. By substituting the - * previous and the following statements (under the - * assumption that |a[i]| is 0), we obtain c[i] / b[i], - * which must be less than 1. */ - cc[i] = c[i] * m; - for (j = 0; j < dim; j++) { - k = i * dim + j; - l = (i - 1) * dim + j; - d[k] = (d[k] - a[i] * d[l]) * m; - } - } - - /* Back substitution. */ - for (i = num - 1; i > 0; i--) { - for (j = 0; j < dim; j++) { - k = (i - 1) * dim + j; - l = i * dim + j; - d[k] -= cc[i - 1] * d[l]; - } - } - TS_FINALLY - free(cc); - TS_END_TRY_RETURN(err) -} - -tsError -ts_int_relaxed_uniform_cubic_bspline(const tsReal *points, - size_t n, - size_t dim, - tsBSpline *spline, - tsStatus *status) { - const size_t order = 4; /**< Order of spline to interpolate. */ - const tsReal as = 1.f / 6.f; /**< The value 'a sixth'. */ - const tsReal at = 1.f / 3.f; /**< The value 'a third'. */ - const tsReal tt = 2.f / 3.f; /**< The value 'two third'. */ - size_t sof_ctrlp; /**< Size of a single control point. */ - const tsReal *b = points; /**< Array of the b values. */ - tsReal *s; /**< Array of the s values. */ - size_t i, d; /**< Used in for loops */ - size_t j, k, l; /**< Used as temporary indices. */ - tsReal *ctrlp; /**< Pointer to the control points of \p _spline_. */ - tsError err; - - /* input validation */ - if (dim == 0) - TS_RETURN_0(status, TS_DIM_ZERO, "unsupported dimension: 0") - if (n <= 1) { - TS_RETURN_1(status, TS_NUM_POINTS, - "num(points) (%lu) <= 1", - (unsigned long)n) - } - /* in the following n >= 2 applies */ - - sof_ctrlp = dim * sizeof(tsReal); /* dim > 0 implies sof_ctrlp > 0 */ - - s = NULL; - TS_TRY(try, err, status) - /* n >= 2 implies n-1 >= 1 implies (n-1)*4 >= 4 */ - TS_CALL(try, err, ts_bspline_new((n - 1) * 4, dim, order - 1, TS_BEZIERS, spline, status)) - ctrlp = ts_int_bspline_access_ctrlp(spline); - - s = (tsReal *)malloc(n * sof_ctrlp); - if (!s) { - TS_THROW_0(try, err, status, TS_MALLOC, - "out of memory") - } - - /* set s_0 to b_0 and s_n = b_n */ - memcpy(s, b, sof_ctrlp); - memcpy(s + (n - 1) * dim, b + (n - 1) * dim, sof_ctrlp); - - /* set s_i = 1/6*b_i + 2/3*b_{i-1} + 1/6*b_{i+1}*/ - for (i = 1; i < n - 1; i++) { - for (d = 0; d < dim; d++) { - j = (i - 1) * dim + d; - k = i * dim + d; - l = (i + 1) * dim + d; - s[k] = as * b[j]; - s[k] += tt * b[k]; - s[k] += as * b[l]; - } - } - - /* create beziers from b and s */ - for (i = 0; i < n - 1; i++) { - for (d = 0; d < dim; d++) { - j = i * dim + d; - k = i * 4 * dim + d; - l = (i + 1) * dim + d; - ctrlp[k] = s[j]; - ctrlp[k + dim] = tt * b[j] + at * b[l]; - ctrlp[k + 2 * dim] = at * b[j] + tt * b[l]; - ctrlp[k + 3 * dim] = s[l]; - } - } - TS_CATCH(err) - ts_bspline_free(spline); - TS_FINALLY - if (s) - free(s); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_interpolate_cubic_natural(const tsReal *points, - size_t num_points, - size_t dimension, - tsBSpline *spline, - tsStatus *status) { - const size_t sof_ctrlp = dimension * sizeof(tsReal); - const size_t len_points = num_points * dimension; - const size_t num_int_points = num_points - 2; - const size_t len_int_points = num_int_points * dimension; - tsReal *thomas, *a, *b, *c, *d; - size_t i, j, k, l; - tsError err; - - ts_int_bspline_init(spline); - if (num_points == 0) - TS_RETURN_0(status, TS_NUM_POINTS, "num(points) == 0") - if (num_points == 1) { - TS_CALL_ROE(err, ts_int_cubic_point( - points, dimension, spline, status)) - TS_RETURN_SUCCESS(status) - } - if (num_points == 2) { - return ts_int_relaxed_uniform_cubic_bspline( - points, num_points, dimension, spline, status); - } - /* `num_points` >= 3 */ - thomas = NULL; - TS_TRY(try, err, status) - thomas = (tsReal *)malloc( - /* `a', `b', `c' (note that `c' is equal to `a') */ - 2 * num_int_points * sizeof(tsReal) + - /* `d' and "result of the thomas algorithm" (which - contains `num_points' points) */ - num_points * dimension * sizeof(tsReal)); - if (!thomas) { - TS_THROW_0(try, err, status, TS_MALLOC, - "out of memory") - } - /* The system of linear equations is taken from: - * http://www.bakoma-tex.com/doc/generic/pst-bspline/ - * pst-bspline-doc.pdf */ - a = c = thomas; - ts_arr_fill(a, num_int_points, 1); - b = a + num_int_points; - ts_arr_fill(b, num_int_points, 4); - d = b + num_int_points; - /* 6 * S_{i+1} */ - for (i = 0; i < num_int_points; i++) { - for (j = 0; j < dimension; j++) { - k = i * dimension + j; - l = (i + 1) * dimension + j; - d[k] = 6 * points[l]; - } - } - for (i = 0; i < dimension; i++) { - /* 6 * S_{1} - S_{0} */ - d[i] -= points[i]; - /* 6 * S_{n-1} - S_{n} */ - k = len_int_points - (i + 1); - l = len_points - (i + 1); - d[k] -= points[l]; - } - /* The Thomas algorithm requires at least two points. Hence, - * `num_int_points` == 1 must be handled separately (let's call - * it "Mini Thomas"). */ - if (num_int_points == 1) { - for (i = 0; i < dimension; i++) - d[i] *= (tsReal)0.25f; - } else { - TS_CALL(try, err, ts_int_thomas_algorithm(a, b, c, num_int_points, dimension, d, status)) - } - memcpy(thomas, points, sof_ctrlp); - memmove(thomas + dimension, d, num_int_points * sof_ctrlp); - memcpy(thomas + (num_int_points + 1) * dimension, - points + (num_points - 1) * dimension, sof_ctrlp); - TS_CALL(try, err, ts_int_relaxed_uniform_cubic_bspline(thomas, num_points, dimension, spline, status)) - TS_CATCH(err) - ts_bspline_free(spline); - TS_FINALLY - if (thomas) - free(thomas); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_interpolate_catmull_rom(const tsReal *points, - size_t num_points, - size_t dimension, - tsReal alpha, - const tsReal *first, - const tsReal *last, - tsReal epsilon, - tsBSpline *spline, - tsStatus *status) { - const size_t sof_real = sizeof(tsReal); - const size_t sof_ctrlp = dimension * sof_real; - const tsReal eps = (tsReal)fabs(epsilon); - tsReal *bs_ctrlp; /* Points to the control points of `spline`. */ - tsReal *cr_ctrlp; /**< The points to interpolate based on `points`. */ - size_t i, d; /**< Used in for loops. */ - tsError err; /**< Local error handling. */ - /* [https://en.wikipedia.org/wiki/ - * Centripetal_Catmull%E2%80%93Rom_spline] */ - tsReal t0, t1, t2, t3; /**< Catmull-Rom knots. */ - /* [https://stackoverflow.com/questions/30748316/ - * catmull-rom-interpolation-on-svg-paths/30826434#30826434] */ - tsReal c1, c2, d1, d2, m1, m2; /**< Used to calculate derivatives. */ - tsReal *p0, *p1, *p2, *p3; /**< Processed Catmull-Rom points. */ - - ts_int_bspline_init(spline); - if (dimension == 0) - TS_RETURN_0(status, TS_DIM_ZERO, "unsupported dimension: 0") - if (num_points == 0) - TS_RETURN_0(status, TS_NUM_POINTS, "num(points) == 0") - if (alpha < (tsReal)0.0) alpha = (tsReal)0.0; - if (alpha > (tsReal)1.0) alpha = (tsReal)1.0; - - /* Copy `points` to `cr_ctrlp`. Add space for `first` and `last`. */ - cr_ctrlp = (tsReal *)malloc((num_points + 2) * sof_ctrlp); - if (!cr_ctrlp) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - memcpy(cr_ctrlp + dimension, points, num_points * sof_ctrlp); - - /* Remove redundant points from `cr_ctrlp`. Update `num_points`. */ - for (i = 1 /* 0 (`first`) is not assigned yet */; - i < num_points /* skip last point (inclusive end) */; - i++) { - p0 = cr_ctrlp + (i * dimension); - p1 = p0 + dimension; - if (ts_distance(p0, p1, dimension) <= eps) { - /* Are there any other points (after the one that is - * to be removed) that need to be shifted? */ - if (i < num_points - 1) { - memmove(p1, p1 + dimension, - (num_points - (i + 1)) * sof_ctrlp); - } - num_points--; - i--; - } - } - - /* Check if there are still enough points for interpolation. */ - if (num_points == 1) { /* `num_points` can't be 0 */ - free(cr_ctrlp); /* The point is copied from `points`. */ - TS_CALL_ROE(err, ts_int_cubic_point( - points, dimension, spline, status)) - TS_RETURN_SUCCESS(status) - } - - /* Add or generate `first` and `last`. Update `num_points`. */ - p0 = cr_ctrlp + dimension; - if (first && ts_distance(first, p0, dimension) > eps) { - memcpy(cr_ctrlp, first, sof_ctrlp); - } else { - p1 = p0 + dimension; - for (d = 0; d < dimension; d++) - cr_ctrlp[d] = p0[d] + (p0[d] - p1[d]); - } - p1 = cr_ctrlp + (num_points * dimension); - if (last && ts_distance(p1, last, dimension) > eps) { - memcpy(cr_ctrlp + ((num_points + 1) * dimension), - last, sof_ctrlp); - } else { - p0 = p1 - dimension; - for (d = 0; d < dimension; d++) { - cr_ctrlp[((num_points + 1) * dimension) + d] = - p1[d] + (p1[d] - p0[d]); - } - } - num_points = num_points + 2; - - /* Transform the sequence of Catmull-Rom splines. */ - bs_ctrlp = NULL; - TS_TRY(try, err, status) - TS_CALL(try, err, ts_bspline_new((num_points - 3) * 4, dimension, 3, TS_BEZIERS, spline, status)) - bs_ctrlp = ts_int_bspline_access_ctrlp(spline); - TS_CATCH(err) - free(cr_ctrlp); - TS_END_TRY_ROE(err) - for (i = 0; i < ts_bspline_num_control_points(spline) / 4; i++) { - p0 = cr_ctrlp + ((i + 0) * dimension); - p1 = cr_ctrlp + ((i + 1) * dimension); - p2 = cr_ctrlp + ((i + 2) * dimension); - p3 = cr_ctrlp + ((i + 3) * dimension); - - t0 = (tsReal)0.f; - t1 = t0 + (tsReal)pow(ts_distance(p0, p1, dimension), alpha); - t2 = t1 + (tsReal)pow(ts_distance(p1, p2, dimension), alpha); - t3 = t2 + (tsReal)pow(ts_distance(p2, p3, dimension), alpha); - - c1 = (t2 - t1) / (t2 - t0); - c2 = (t1 - t0) / (t2 - t0); - d1 = (t3 - t2) / (t3 - t1); - d2 = (t2 - t1) / (t3 - t1); - - for (d = 0; d < dimension; d++) { - m1 = (t2 - t1) * (c1 * (p1[d] - p0[d]) / (t1 - t0) + c2 * (p2[d] - p1[d]) / (t2 - t1)); - m2 = (t2 - t1) * (d1 * (p2[d] - p1[d]) / (t2 - t1) + d2 * (p3[d] - p2[d]) / (t3 - t2)); - bs_ctrlp[((i * 4 + 0) * dimension) + d] = p1[d]; - bs_ctrlp[((i * 4 + 1) * dimension) + d] = p1[d] + m1 / 3; - bs_ctrlp[((i * 4 + 2) * dimension) + d] = p2[d] - m2 / 3; - bs_ctrlp[((i * 4 + 3) * dimension) + d] = p2[d]; - } - } - free(cr_ctrlp); - TS_RETURN_SUCCESS(status) -} -/*! @} */ - -/*! @name Query Functions - * - * @{ - */ -tsError -ts_int_bspline_find_knot(const tsBSpline *spline, - tsReal knot, - size_t *index, - size_t *multiplicity, - tsStatus *status) { - const size_t deg = ts_bspline_degree(spline); - const size_t num_knots = ts_bspline_num_knots(spline); - const tsReal *knots = ts_int_bspline_access_knots(spline); - tsReal min, max; - size_t low, high; - - ts_bspline_domain(spline, &min, &max); - if (knot < min && !ts_knots_equal(knot, min)) { - TS_RETURN_2(status, TS_U_UNDEFINED, - "knot (%f) < min(domain) (%f)", - knot, min) - } - if (knot > max && !ts_knots_equal(knot, max)) { - TS_RETURN_2(status, TS_U_UNDEFINED, - "knot (%f) > max(domain) (%f)", - knot, max) - } - - /* Based on 'The NURBS Book' (Les Piegl and Wayne Tiller). */ - if (ts_knots_equal(knot, knots[num_knots - 1])) { - *index = num_knots - 1; - } else { - low = 0; - high = num_knots - 1; - *index = (low + high) / 2; - while (knot < knots[*index] || knot >= knots[*index + 1]) { - if (knot < knots[*index]) - high = *index; - else - low = *index; - *index = (low + high) / 2; - } - } - - /* Handle floating point errors. */ - while (*index < num_knots - 1 && /* there is a next knot */ - ts_knots_equal(knot, knots[*index + 1])) { - (*index)++; - } - - /* Calculate knot's multiplicity. */ - for (*multiplicity = deg + 1; *multiplicity > 0; (*multiplicity)--) { - if (ts_knots_equal(knot, knots[*index - (*multiplicity - 1)])) - break; - } - - TS_RETURN_SUCCESS(status) -} - -tsError -ts_int_bspline_eval_woa(const tsBSpline *spline, - tsReal u, - tsDeBoorNet *net, - tsStatus *status) { - const size_t deg = ts_bspline_degree(spline); - const size_t order = ts_bspline_order(spline); - const size_t dim = ts_bspline_dimension(spline); - const size_t num_knots = ts_bspline_num_knots(spline); - const size_t sof_ctrlp = dim * sizeof(tsReal); - - const tsReal *ctrlp = ts_int_bspline_access_ctrlp(spline); - const tsReal *knots = ts_int_bspline_access_knots(spline); - tsReal *points = NULL; /**< Pointer to the points of \p net. */ - - size_t k; /**< Index of \p u. */ - size_t s; /**< Multiplicity of \p u. */ - - tsReal uk; /**< The actual used u. */ - size_t from; /**< Offset used to copy values. */ - size_t fst; /**< First affected control point, inclusive. */ - size_t lst; /**< Last affected control point, inclusive. */ - size_t N; /**< Number of affected control points. */ - - /* The following indices are used to create the DeBoor net. */ - size_t lidx; /**< Current left index. */ - size_t ridx; /**< Current right index. */ - size_t tidx; /**< Current to index. */ - size_t r, i, d; /**< Used in for loop. */ - tsReal ui; /**< Knot value at index i. */ - tsReal a, a_hat; /**< Weighting factors of control points. */ - - tsError err; - - points = ts_int_deboornet_access_points(net); - - /* 1. Find index k such that u is in between [u_k, u_k+1). - * 2. Setup already known values. - * 3. Decide by multiplicity of u how to calculate point P(u). */ - - /* 1. */ - k = s = 0; - TS_CALL_ROE(err, ts_int_bspline_find_knot( - spline, u, &k, &s, status)) - - /* 2. */ - uk = knots[k]; /* Ensures that with any precision of */ - net->pImpl->u = /* tsReal the knot vector stays valid. */ - ts_knots_equal(u, uk) ? uk : u; - net->pImpl->k = k; - net->pImpl->s = s; - net->pImpl->h = deg < s ? 0 : deg - s; /* prevent underflow */ - - /* 3. (by 1. s <= order) - * - * 3a) Check for s = order. - * Take the two points k-s and k-s + 1. If one of - * them doesn't exist, take only the other. - * 3b) Use de boor algorithm to find point P(u). */ - if (s == order) { - /* only one of the two control points exists */ - if (k == deg || /* only the first */ - k == num_knots - 1) { /* only the last */ - from = k == deg ? 0 : (k - s) * dim; - net->pImpl->n_points = 1; - memcpy(points, ctrlp + from, sof_ctrlp); - } else { - from = (k - s) * dim; - net->pImpl->n_points = 2; - memcpy(points, ctrlp + from, 2 * sof_ctrlp); - } - } else { /* by 3a) s <= deg (order = deg+1) */ - fst = k - deg; /* by 1. k >= deg */ - lst = k - s; /* s <= deg <= k */ - N = lst - fst + 1; /* lst <= fst implies N >= 1 */ - - net->pImpl->n_points = (size_t)(N * (N + 1) * 0.5f); - - /* copy initial values to output */ - memcpy(points, ctrlp + fst * dim, N * sof_ctrlp); - - lidx = 0; - ridx = dim; - tidx = N * dim; /* N >= 1 implies tidx > 0 */ - r = 1; - for (; r <= ts_deboornet_num_insertions(net); r++) { - i = fst + r; - for (; i <= lst; i++) { - ui = knots[i]; - a = (ts_deboornet_knot(net) - ui) / - (knots[i + deg - r + 1] - ui); - a_hat = 1.f - a; - - for (d = 0; d < dim; d++) { - points[tidx++] = - a_hat * points[lidx++] + - a * points[ridx++]; - } - } - lidx += dim; - ridx += dim; - } - } - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_eval(const tsBSpline *spline, - tsReal knot, - tsDeBoorNet *net, - tsStatus *status) { - tsError err; - ts_int_deboornet_init(net); - TS_TRY(try, err, status) - TS_CALL(try, err, ts_int_deboornet_new(spline, net, status)) - TS_CALL(try, err, ts_int_bspline_eval_woa(spline, knot, net, status)) - TS_CATCH(err) - ts_deboornet_free(net); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_eval_all(const tsBSpline *spline, - const tsReal *knots, - size_t num, - tsReal **points, - tsStatus *status) { - const size_t dim = ts_bspline_dimension(spline); - const size_t sof_point = dim * sizeof(tsReal); - const size_t sof_points = num * sof_point; - tsDeBoorNet net = ts_deboornet_init(); - tsReal *result; - size_t i; - tsError err; - TS_TRY(try, err, status) - *points = (tsReal *)malloc(sof_points); - if (!*points) { - TS_THROW_0(try, err, status, TS_MALLOC, - "out of memory") - } - TS_CALL(try, err, ts_int_deboornet_new(spline, &net, status)) - for (i = 0; i < num; i++) { - TS_CALL(try, err, ts_int_bspline_eval_woa(spline, knots[i], &net, status)) - result = ts_int_deboornet_access_result(&net); - memcpy((*points) + i * dim, result, sof_point); - } - TS_CATCH(err) - if (*points) - free(*points); - *points = NULL; - TS_FINALLY - ts_deboornet_free(&net); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_sample(const tsBSpline *spline, - size_t num, - tsReal **points, - size_t *actual_num, - tsStatus *status) { - tsError err; - tsReal *knots; - - num = num == 0 ? 100 : num; - *actual_num = num; - knots = (tsReal *)malloc(num * sizeof(tsReal)); - if (!knots) { - *points = NULL; - TS_RETURN_0(status, TS_MALLOC, "out of memory") - } - ts_bspline_uniform_knot_seq(spline, num, knots); - TS_TRY(try, err, status) - TS_CALL(try, err, ts_bspline_eval_all(spline, knots, num, points, status)) - TS_FINALLY - free(knots); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_bisect(const tsBSpline *spline, - tsReal value, - tsReal epsilon, - int persnickety, - size_t index, - int ascending, - size_t max_iter, - tsDeBoorNet *net, - tsStatus *status) { - tsError err; - const size_t dim = ts_bspline_dimension(spline); - const tsReal eps = (tsReal)fabs(epsilon); - size_t i = 0; - tsReal dist = 0; - tsReal min, max, mid; - tsReal *P; - - ts_int_deboornet_init(net); - - if (dim < index) { - TS_RETURN_2(status, TS_INDEX_ERROR, - "dimension (%lu) <= index (%lu)", - (unsigned long)dim, - (unsigned long)index) - } - if (max_iter == 0) - TS_RETURN_0(status, TS_NO_RESULT, "0 iterations") - - ts_bspline_domain(spline, &min, &max); - TS_TRY(try, err, status) - TS_CALL(try, err, ts_int_deboornet_new(spline, net, status)) - do { - mid = (tsReal)((min + max) / 2.0); - TS_CALL(try, err, ts_int_bspline_eval_woa(spline, mid, net, status)) - P = ts_int_deboornet_access_result(net); - dist = ts_distance(&P[index], &value, 1); - if (dist <= eps) - TS_RETURN_SUCCESS(status) - if (ascending) { - if (P[index] < value) - min = mid; - else - max = mid; - } else { - if (P[index] < value) - max = mid; - else - min = mid; - } - } while (i++ < max_iter); - if (persnickety) { - TS_THROW_1(try, err, status, TS_NO_RESULT, - "maximum iterations (%lu) exceeded", - (unsigned long)max_iter) - } - TS_CATCH(err) - ts_deboornet_free(net); - TS_END_TRY_RETURN(err) -} - -void ts_bspline_domain(const tsBSpline *spline, - tsReal *min, - tsReal *max) { - *min = ts_int_bspline_access_knots(spline) - [ts_bspline_degree(spline)]; - *max = ts_int_bspline_access_knots(spline) - [ts_bspline_num_knots(spline) - ts_bspline_order(spline)]; -} - -tsError -ts_bspline_is_closed(const tsBSpline *spline, - tsReal epsilon, - int *closed, - tsStatus *status) { - const size_t deg = ts_bspline_degree(spline); - const size_t dim = ts_bspline_dimension(spline); - tsBSpline derivative; - tsReal min, max; - tsDeBoorNet first, last; - size_t i; - tsError err; - - ts_int_bspline_init(&derivative); - ts_int_deboornet_init(&first); - ts_int_deboornet_init(&last); - - TS_TRY(try, err, status) - for (i = 0; i < deg; i++) { - TS_CALL(try, err, ts_bspline_derive(spline, i, -1.f, &derivative, status)) - ts_bspline_domain(&derivative, &min, &max); - TS_CALL(try, err, ts_bspline_eval(&derivative, min, &first, status)) - TS_CALL(try, err, ts_bspline_eval(&derivative, max, &last, status)) - *closed = ts_distance( - ts_int_deboornet_access_result(&first), - ts_int_deboornet_access_result(&last), - dim) <= epsilon - ? 1 - : 0; - ts_bspline_free(&derivative); - ts_deboornet_free(&first); - ts_deboornet_free(&last); - if (!*closed) - TS_RETURN_SUCCESS(status) - } - TS_FINALLY - ts_bspline_free(&derivative); - ts_deboornet_free(&first); - ts_deboornet_free(&last); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_compute_rmf(const tsBSpline *spline, - const tsReal *knots, - size_t num, - int has_first_normal, - tsFrame *frames, - tsStatus *status) { - tsError err; - size_t i; - tsReal fx, fy, fz, fmin; - tsReal xc[3], xn[3], v1[3], c1, v2[3], c2, rL[3], tL[3]; - tsBSpline deriv = ts_bspline_init(); - tsDeBoorNet curr = ts_deboornet_init(); - tsDeBoorNet next = ts_deboornet_init(); - - if (num < 1) - TS_RETURN_SUCCESS(status); - - TS_TRY(try, err, status) - TS_CALL(try, err, ts_int_deboornet_new(spline, &curr, status)) - TS_CALL(try, err, ts_int_deboornet_new(spline, &next, status)) - TS_CALL(try, err, ts_bspline_derive(spline, 1, (tsReal)-1.0, &deriv, status)) - - /* Set position. */ - TS_CALL(try, err, ts_int_bspline_eval_woa(spline, knots[0], &curr, status)) - ts_vec3_set(frames[0].position, - ts_int_deboornet_access_result(&curr), - ts_bspline_dimension(spline)); - /* Set tangent. */ - TS_CALL(try, err, ts_int_bspline_eval_woa(&deriv, knots[0], &curr, status)) - ts_vec3_set(frames[0].tangent, - ts_int_deboornet_access_result(&curr), - ts_bspline_dimension(&deriv)); - ts_vec_norm(frames[0].tangent, 3, frames[0].tangent); - /* Set normal. */ - if (!has_first_normal) { - fx = (tsReal)fabs(frames[0].tangent[0]); - fy = (tsReal)fabs(frames[0].tangent[1]); - fz = (tsReal)fabs(frames[0].tangent[2]); - fmin = fx; /* x is min => 1, 0, 0 */ - ts_vec3_init(frames[0].normal, - (tsReal)1.0, - (tsReal)0.0, - (tsReal)0.0); - if (fy < fmin) { /* y is min => 0, 1, 0 */ - fmin = fy; - ts_vec3_init(frames[0].normal, - (tsReal)0.0, - (tsReal)1.0, - (tsReal)0.0); - } - if (fz < fmin) { /* z is min => 0, 0, 1 */ - ts_vec3_init(frames[0].normal, - (tsReal)0.0, - (tsReal)0.0, - (tsReal)1.0); - } - ts_vec3_cross(frames[0].tangent, - frames[0].normal, - frames[0].normal); - ts_vec_norm(frames[0].normal, 3, frames[0].normal); - ts_vec3_cross(frames[0].tangent, - frames[0].normal, - frames[0].normal); - } else { - /* Never trust user input! */ - ts_vec_norm(frames[0].normal, 3, frames[0].normal); - } - /* Set binormal. */ - ts_vec3_cross(frames[0].tangent, - frames[0].normal, - frames[0].binormal); - - for (i = 0; i < num - 1; i++) { - /* Eval current and next point. */ - TS_CALL(try, err, ts_int_bspline_eval_woa(spline, knots[i], &curr, status)) - TS_CALL(try, err, ts_int_bspline_eval_woa(spline, knots[i + 1], &next, status)) - ts_vec3_set(xc, /* xc is now the current point */ - ts_int_deboornet_access_result(&curr), - ts_bspline_dimension(spline)); - ts_vec3_set(xn, /* xn is now the next point */ - ts_int_deboornet_access_result(&next), - ts_bspline_dimension(spline)); - - /* Set position of U_{i+1}. */ - ts_vec3_set(frames[i + 1].position, xn, 3); - - /* Compute reflection vector of R_{1}. */ - ts_vec_sub(xn, xc, 3, v1); - c1 = ts_vec_dot(v1, v1, 3); - - /* Compute r_{i}^{L} = R_{1} * r_{i}. */ - rL[0] = (tsReal)2.0 / c1; - rL[1] = ts_vec_dot(v1, frames[i].normal, 3); - rL[2] = rL[0] * rL[1]; - ts_vec_mul(v1, 3, rL[2], rL); - ts_vec_sub(frames[i].normal, rL, 3, rL); - - /* Compute t_{i}^{L} = R_{1} * t_{i}. */ - tL[0] = (tsReal)2.0 / c1; - tL[1] = ts_vec_dot(v1, frames[i].tangent, 3); - tL[2] = tL[0] * tL[1]; - ts_vec_mul(v1, 3, tL[2], tL); - ts_vec_sub(frames[i].tangent, tL, 3, tL); - - /* Compute reflection vector of R_{2}. */ - TS_CALL(try, err, ts_int_bspline_eval_woa(&deriv, knots[i + 1], &next, status)) - ts_vec3_set(xn, /* xn is now the next tangent */ - ts_int_deboornet_access_result(&next), - ts_bspline_dimension(&deriv)); - ts_vec_norm(xn, 3, xn); - ts_vec_sub(xn, tL, 3, v2); - c2 = ts_vec_dot(v2, v2, 3); - - /* Compute r_{i+1} = R_{2} * r_{i}^{L}. */ - ts_vec3_set(xc, /* xc is now the next normal */ - frames[i + 1].normal, 3); - xc[0] = (tsReal)2.0 / c2; - xc[1] = ts_vec_dot(v2, rL, 3); - xc[2] = xc[0] * xc[1]; - ts_vec_mul(v2, 3, xc[2], xc); - ts_vec_sub(rL, xc, 3, xc); - ts_vec_norm(xc, 3, xc); - - /* Compute vector s_{i+1} of U_{i+1}. */ - ts_vec3_cross(xn, xc, frames[i + 1].binormal); - - /* Set vectors t_{i+1} and r_{i+1} of U_{i+1}. */ - ts_vec3_set(frames[i + 1].tangent, xn, 3); - ts_vec3_set(frames[i + 1].normal, xc, 3); - } - TS_FINALLY - ts_bspline_free(&deriv); - ts_deboornet_free(&curr); - ts_deboornet_free(&next); - TS_END_TRY_RETURN(err) -} - -void ts_bspline_uniform_knot_seq(const tsBSpline *spline, - size_t num, - tsReal *knots) { - size_t i; - tsReal min, max; - ts_bspline_domain(spline, &min, &max); - for (i = 0; i < num; i++) { - knots[i] = max - min; - knots[i] *= (tsReal)i / (num - 1); - knots[i] += min; - } - /* Set knots[0] after knots[num - 1] to ensure that - * knots[0] = min if num is 1. */ - knots[num - 1] = max; - knots[0] = min; -} -/*! @} */ - -/*! @name Transformation Functions - * - * @{ - */ -tsError -ts_int_bspline_resize(const tsBSpline *spline, - int n, - int back, - tsBSpline *resized, - tsStatus *status) { - const size_t deg = ts_bspline_degree(spline); - const size_t dim = ts_bspline_dimension(spline); - const size_t sof_real = sizeof(tsReal); - - const size_t num_ctrlp = ts_bspline_num_control_points(spline); - const size_t num_knots = ts_bspline_num_knots(spline); - const size_t nnum_ctrlp = num_ctrlp + n; /**< New length of ctrlp. */ - const size_t nnum_knots = num_knots + n; /**< New length of knots. */ - const size_t min_num_ctrlp_vec = n < 0 ? nnum_ctrlp : num_ctrlp; - const size_t min_num_knots_vec = n < 0 ? nnum_knots : num_knots; - - const size_t sof_min_num_ctrlp = min_num_ctrlp_vec * dim * sof_real; - const size_t sof_min_num_knots = min_num_knots_vec * sof_real; - - tsBSpline tmp; /**< Temporarily stores the result. */ - const tsReal *from_ctrlp = ts_int_bspline_access_ctrlp(spline); - const tsReal *from_knots = ts_int_bspline_access_knots(spline); - tsReal *to_ctrlp = NULL; /**< Pointer to the control points of tmp. */ - tsReal *to_knots = NULL; /**< Pointer to the knots of tmp. */ - - tsError err; - - if (n == 0) - return ts_bspline_copy(spline, resized, status); - - INIT_OUT_BSPLINE(spline, resized) - TS_CALL_ROE(err, ts_bspline_new( - nnum_ctrlp, dim, deg, TS_OPENED, - &tmp, status)) - to_ctrlp = ts_int_bspline_access_ctrlp(&tmp); - to_knots = ts_int_bspline_access_knots(&tmp); - - /* Copy control points and knots. */ - if (!back && n < 0) { - memcpy(to_ctrlp, from_ctrlp - n * dim, sof_min_num_ctrlp); - memcpy(to_knots, from_knots - n, sof_min_num_knots); - } else if (!back && n > 0) { - memcpy(to_ctrlp + n * dim, from_ctrlp, sof_min_num_ctrlp); - memcpy(to_knots + n, from_knots, sof_min_num_knots); - } else { - /* n != 0 implies back == true */ - memcpy(to_ctrlp, from_ctrlp, sof_min_num_ctrlp); - memcpy(to_knots, from_knots, sof_min_num_knots); - } - - if (spline == resized) - ts_bspline_free(resized); - ts_bspline_move(&tmp, resized); - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_derive(const tsBSpline *spline, - size_t n, - tsReal epsilon, - tsBSpline *deriv, - tsStatus *status) { - const size_t sof_real = sizeof(tsReal); - const size_t dim = ts_bspline_dimension(spline); - const size_t sof_ctrlp = dim * sof_real; - size_t deg = ts_bspline_degree(spline); - size_t num_ctrlp = ts_bspline_num_control_points(spline); - size_t num_knots = ts_bspline_num_knots(spline); - - tsBSpline worker; /**< Stores the intermediate result. */ - tsReal *ctrlp; /**< Pointer to the control points of worker. */ - tsReal *knots; /**< Pointer to the knots of worker. */ - - size_t m, i, j, k, l; /**< Used in for loops. */ - tsReal *fst, *snd; /**< Pointer to first and second control point. */ - tsReal dist; /**< Distance between fst and snd. */ - tsReal kid1, ki1; /**< Knots at i+deg+1 and i+1. */ - tsReal span; /**< Distance between kid1 and ki1. */ - - tsBSpline swap; /**< Used to swap worker and derivative. */ - tsError err; - - INIT_OUT_BSPLINE(spline, deriv) - TS_CALL_ROE(err, ts_bspline_copy(spline, &worker, status)) - ctrlp = ts_int_bspline_access_ctrlp(&worker); - knots = ts_int_bspline_access_knots(&worker); - - TS_TRY(try, err, status) - for (m = 1; m <= n; m++) { /* from 1st to n'th derivative */ - if (deg == 0) { - ts_arr_fill(ctrlp, dim, 0.f); - ts_bspline_domain(spline, - &knots[0], - &knots[1]); - num_ctrlp = 1; - num_knots = 2; - break; - } - /* Check and, if possible, fix discontinuity. */ - for (i = 2 * deg + 1; i < num_knots - (deg + 1); i++) { - if (!ts_knots_equal(knots[i], knots[i - deg])) - continue; - fst = ctrlp + (i - (deg + 1)) * dim; - snd = fst + dim; - dist = ts_distance(fst, snd, dim); - if (epsilon >= 0.f && dist > epsilon) { - TS_THROW_1(try, err, status, - TS_UNDERIVABLE, - "discontinuity at knot: %f", - knots[i]) - } - memmove(snd, - snd + dim, - (num_ctrlp - (i + 1 - deg)) * sof_ctrlp); - memmove(&knots[i], - &knots[i + 1], - (num_knots - (i + 1)) * sof_real); - num_ctrlp--; - num_knots--; - i += deg - 1; - } - /* Derive continuous worker. */ - for (i = 0; i < num_ctrlp - 1; i++) { - for (j = 0; j < dim; j++) { - k = i * dim + j; - l = (i + 1) * dim + j; - ctrlp[k] = ctrlp[l] - ctrlp[k]; - kid1 = knots[i + deg + 1]; - ki1 = knots[i + 1]; - span = kid1 - ki1; - if (span < TS_KNOT_EPSILON) - span = TS_KNOT_EPSILON; - ctrlp[k] *= deg; - ctrlp[k] /= span; - } - } - deg -= 1; - num_ctrlp -= 1; - num_knots -= 2; - knots += 1; - } - TS_CALL(try, err, ts_bspline_new(num_ctrlp, dim, deg, TS_OPENED, &swap, status)) - memcpy(ts_int_bspline_access_ctrlp(&swap), - ctrlp, - num_ctrlp * sof_ctrlp); - memcpy(ts_int_bspline_access_knots(&swap), - knots, - num_knots * sof_real); - if (spline == deriv) - ts_bspline_free(deriv); - ts_bspline_move(&swap, deriv); - TS_FINALLY - ts_bspline_free(&worker); - TS_END_TRY_RETURN(err) -} - -tsError -ts_int_bspline_insert_knot(const tsBSpline *spline, - const tsDeBoorNet *net, - size_t n, - tsBSpline *result, - tsStatus *status) { - const size_t deg = ts_bspline_degree(spline); - const size_t order = ts_bspline_order(spline); - const size_t dim = ts_bspline_dimension(spline); - const tsReal knot = ts_deboornet_knot(net); - const size_t k = ts_deboornet_index(net); - const size_t mult = ts_deboornet_multiplicity(net); - const size_t sof_real = sizeof(tsReal); - const size_t sof_ctrlp = dim * sof_real; - - size_t N; /**< Number of affected control points. */ - tsReal *from; /**< Pointer to copy the values from. */ - tsReal *to; /**< Pointer to copy the values to. */ - int stride; /**< Stride of the next pointer to copy. */ - size_t i; /**< Used in for loops. */ - - tsReal *ctrlp_spline, *ctrlp_result; - tsReal *knots_spline, *knots_result; - size_t num_ctrlp_result; - size_t num_knots_result; - - tsError err; - - INIT_OUT_BSPLINE(spline, result) - if (n == 0) - return ts_bspline_copy(spline, result, status); - if (mult + n > order) { - TS_RETURN_4(status, TS_MULTIPLICITY, - "multiplicity(%f) (%lu) + %lu > order (%lu)", - knot, (unsigned long)mult, (unsigned long)n, - (unsigned long)order) - } - - TS_CALL_ROE(err, ts_int_bspline_resize( - spline, (int)n, 1, result, status)) - ctrlp_spline = ts_int_bspline_access_ctrlp(spline); - knots_spline = ts_int_bspline_access_knots(spline); - ctrlp_result = ts_int_bspline_access_ctrlp(result); - knots_result = ts_int_bspline_access_knots(result); - num_ctrlp_result = ts_bspline_num_control_points(result); - num_knots_result = ts_bspline_num_knots(result); - - /* mult + n <= deg + 1 (order) with n >= 1 - * => mult <= deg - * => regular evaluation - * => N = h + 1 is valid */ - N = ts_deboornet_num_insertions(net) + 1; - - /* 1. Copy the relevant control points and knots from `spline'. - * 2. Copy the relevant control points and knots from `net'. */ - - /* 1. - * - * a) Copy left hand side control points from `spline'. - * b) Copy right hand side control points from `spline'. - * c) Copy left hand side knots from `spline'. - * d) Copy right hand side knots form `spline'. */ - /*** Copy Control Points ***/ - memmove(ctrlp_result, ctrlp_spline, (k - deg) * sof_ctrlp); /* a) */ - from = (tsReal *)ctrlp_spline + dim * (k - deg + N); - /* n >= 0 implies to >= from */ - to = ctrlp_result + dim * (k - deg + N + n); - memmove(to, from, (num_ctrlp_result - n - (k - deg + N)) * sof_ctrlp); /* b) */ - /*** Copy Knots ***/ - memmove(knots_result, knots_spline, (k + 1) * sof_real); /* c) */ - from = (tsReal *)knots_spline + k + 1; - /* n >= 0 implies to >= from */ - to = knots_result + k + 1 + n; - memmove(to, from, (num_knots_result - n - (k + 1)) * sof_real); /* d) */ - - /* 2. - * - * a) Copy left hand side control points from `net'. - * b) Copy middle part control points from `net'. - * c) Copy right hand side control points from `net'. - * d) Copy knot from `net' (`knot'). */ - from = ts_int_deboornet_access_points(net); - to = ctrlp_result + (k - deg) * dim; - stride = (int)(N * dim); - - /*** Copy Control Points ***/ - for (i = 0; i < n; i++) { /* a) */ - memcpy(to, from, sof_ctrlp); - from += stride; - to += dim; - stride -= (int)dim; - } - memcpy(to, from, (N - n) * sof_ctrlp); /* b) */ - - from -= dim; - to += (N - n) * dim; - /* N = h+1 with h = deg-mult (ts_int_bspline_eval) - * => N = deg-mult+1 = order-mult. - * - * n <= order-mult - * => N-n+1 >= order-mult - order-mult + 1 = 1 - * => -(int)(N-n+1) <= -1. */ - stride = -(int)(N - n + 1) * (int)dim; - - for (i = 0; i < n; i++) { /* c) */ - memcpy(to, from, sof_ctrlp); - from += stride; - stride -= (int)dim; - to += dim; - } - /*** Copy Knot ***/ - to = knots_result + k + 1; - for (i = 0; i < n; i++) { /* d) */ - *to = knot; - to++; - } - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_insert_knot(const tsBSpline *spline, - tsReal knot, - size_t num, - tsBSpline *result, - size_t *k, - tsStatus *status) { - tsDeBoorNet net; - tsError err; - INIT_OUT_BSPLINE(spline, result) - ts_int_deboornet_init(&net); - TS_TRY(try, err, status) - TS_CALL(try, err, ts_bspline_eval(spline, knot, &net, status)) - TS_CALL(try, err, ts_int_bspline_insert_knot(spline, &net, num, result, status)) - ts_deboornet_free(&net); - TS_CALL(try, err, ts_bspline_eval(result, knot, &net, status)) - *k = ts_deboornet_index(&net); - TS_CATCH(err) - *k = 0; - TS_FINALLY - ts_deboornet_free(&net); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_split(const tsBSpline *spline, - tsReal knot, - tsBSpline *split, - size_t *k, - tsStatus *status) { - tsDeBoorNet net; - tsError err; - INIT_OUT_BSPLINE(spline, split) - ts_int_deboornet_init(&net); - TS_TRY(try, err, status) - TS_CALL(try, err, ts_bspline_eval(spline, knot, &net, status)) - if (ts_deboornet_multiplicity(&net) == ts_bspline_order(spline)) { - TS_CALL(try, err, ts_bspline_copy(spline, split, status)) - *k = ts_deboornet_index(&net); - } else { - TS_CALL(try, err, ts_int_bspline_insert_knot(spline, &net, ts_deboornet_num_insertions(&net) + 1, split, status)) - *k = ts_deboornet_index(&net) + - ts_deboornet_num_insertions(&net) + 1; - } - TS_CATCH(err) - *k = 0; - TS_FINALLY - ts_deboornet_free(&net); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_tension(const tsBSpline *spline, - tsReal beta, - tsBSpline *out, - tsStatus *status) { - const size_t dim = ts_bspline_dimension(spline); - const size_t N = ts_bspline_num_control_points(spline); - const tsReal *p0 = ts_int_bspline_access_ctrlp(spline); - const tsReal *pn_1 = p0 + (N - 1) * dim; - - tsReal s; /**< The straightening factor. */ - tsReal *ctrlp; /**< Pointer to the control points of `out'. */ - size_t i, d; /**< Used in for loops. */ - tsReal vec; /**< Straightening vector. */ - tsError err; - - TS_CALL_ROE(err, ts_bspline_copy(spline, out, status)) - ctrlp = ts_int_bspline_access_ctrlp(out); - if (beta < (tsReal)0.0) beta = (tsReal)0.0; - if (beta > (tsReal)1.0) beta = (tsReal)1.0; - s = 1.f - beta; - - for (i = 0; i < N; i++) { - for (d = 0; d < dim; d++) { - vec = ((tsReal)i / (N - 1)) * (pn_1[d] - p0[d]); - ctrlp[i * dim + d] = beta * ctrlp[i * dim + d] + - s * (p0[d] + vec); - } - } - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_to_beziers(const tsBSpline *spline, - tsBSpline *beziers, - tsStatus *status) { - const size_t deg = ts_bspline_degree(spline); - const size_t order = ts_bspline_order(spline); - - int resize; /**< Number of control points to add/remove. */ - size_t k; /**< Index of the split knot value. */ - tsReal u_min; /**< Minimum of the knot values. */ - tsReal u_max; /**< Maximum of the knot values. */ - - tsBSpline tmp; /**< Temporarily stores the result. */ - tsReal *knots; /**< Pointer to the knots of tmp. */ - size_t num_knots; /**< Number of knots in tmp. */ - - tsError err; - - INIT_OUT_BSPLINE(spline, beziers) - TS_CALL_ROE(err, ts_bspline_copy(spline, &tmp, status)) - knots = ts_int_bspline_access_knots(&tmp); - num_knots = ts_bspline_num_knots(&tmp); - - TS_TRY(try, err, status) - /* DO NOT FORGET TO UPDATE knots AND num_knots AFTER EACH - * TRANSFORMATION OF tmp! */ - - /* Fix first control point if necessary. */ - u_min = knots[deg]; - if (!ts_knots_equal(knots[0], u_min)) { - TS_CALL(try, err, ts_bspline_split(&tmp, u_min, &tmp, &k, status)) - resize = (int)(-1 * deg + (deg * 2 - k)); - TS_CALL(try, err, ts_int_bspline_resize(&tmp, resize, 0, &tmp, status)) - knots = ts_int_bspline_access_knots(&tmp); - num_knots = ts_bspline_num_knots(&tmp); - } - - /* Fix last control point if necessary. */ - u_max = knots[num_knots - order]; - if (!ts_knots_equal(knots[num_knots - 1], u_max)) { - TS_CALL(try, err, ts_bspline_split(&tmp, u_max, &tmp, &k, status)) - num_knots = ts_bspline_num_knots(&tmp); - resize = (int)(-1 * deg + (k - (num_knots - order))); - TS_CALL(try, err, ts_int_bspline_resize(&tmp, resize, 1, &tmp, status)) - knots = ts_int_bspline_access_knots(&tmp); - num_knots = ts_bspline_num_knots(&tmp); - } - - /* Split internal knots. */ - k = order; - while (k < num_knots - order) { - TS_CALL(try, err, ts_bspline_split(&tmp, knots[k], &tmp, &k, status)) - knots = ts_int_bspline_access_knots(&tmp); - num_knots = ts_bspline_num_knots(&tmp); - k++; - } - - if (spline == beziers) - ts_bspline_free(beziers); - ts_bspline_move(&tmp, beziers); - TS_FINALLY - ts_bspline_free(&tmp); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_elevate_degree(const tsBSpline *spline, - size_t amount, - tsReal epsilon, - tsBSpline *elevated, - tsStatus *status) { - tsBSpline worker; - size_t dim, order; - tsReal *ctrlp, *knots; - size_t num_beziers, i, a, c, d, offset, idx; - tsReal f, f_hat, *first, *last; - tsError err; - - /* Trivial case. */ - if (amount == 0) - return ts_bspline_copy(spline, elevated, status); - - /* An overview of this algorithm can be found at: - * https://pages.mtu.edu/~shene/COURSES/cs3621/LAB/curve/elevation.html */ - INIT_OUT_BSPLINE(spline, elevated); - worker = ts_bspline_init(); - TS_TRY(try, err, status) - /* Decompose `spline' into a sequence of bezier curves and make - * space for the additional control points and knots that are - * to be inserted. Results are stored in `worker'. */ - TS_CALL(try, err, ts_bspline_to_beziers(spline, &worker, status)); - num_beziers = ts_bspline_num_control_points(&worker) / - ts_bspline_order(&worker); - TS_CALL(try, err, ts_int_bspline_resize( - /* Resize by the number of knots to be inserted. Note - * that this creates too many control points (due to - * increasing the degree), which are removed at the end - * of this function. */ - &worker, (int)((num_beziers + 1) * amount), 1, &worker, status)); - dim = ts_bspline_dimension(&worker); - order = ts_bspline_order(&worker); - ctrlp = ts_int_bspline_access_ctrlp(&worker); - knots = ts_int_bspline_access_knots(&worker); - - /* Move all but the first bezier curve to their new location in - * the control point array so that the additional control - * points can be inserted without overwriting the others. Note - * that iteration must run backwards. Otherwise, the moved - * values overwrite each other. */ - for (i = num_beziers - 1; i > 0; i--) { - /* `i' can be interpreted as the number of bezier - * curves before the current bezier curve. */ - - /* Location of current bezier curve. */ - offset = i * order * dim; - /* Each elevation inserts an additional control point - * into every bezier curve. `i * amount' is the total - * number of control points to be inserted before the - * current bezier curve. */ - memmove(ctrlp + offset + (i * amount * dim), - ctrlp + offset, - dim * order * sizeof(tsReal)); - } - - /* Move all but the first group of knots to their new location - * in the knot vector so that the additional knots can be - * inserted without overwriting the others. Note that iteration - * must run backwards. Otherwise, the moved values overwrite - * each other. */ - for (i = num_beziers; i > 0; i--) { - /* Note that the number of knot groups is one more than - * the number of bezier curves. `i' can be interpreted - * as the number of knot groups before the current - * group. */ - - /* Location of current knot group. */ - offset = i * order; - /* Each elevation inserts an additional knot into every - * group of knots. `i * amount' is the total number of - * knots to be inserted before the current knot - * group. */ - memmove(knots + offset + (i * amount), - knots + offset, - order * sizeof(tsReal)); - } - - /* `worker' is now fully set up. - * The following formulas are based on: - * https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-elev.html */ - for (a = 0; a < amount; a++) { - /* For each bezier curve... */ - for (i = 0; i < num_beziers; i++) { - /* ... 1) Insert and update control points. */ - - /* Location of current bezier curve. Each - * elevation (`a') inserts an additional - * control point into every bezier curve and - * increases the degree (`order') by one. The - * location is thus made up of two parts: - * - * i) `i * order', which is the location taking - * into account the increasing order but - * neglecting the control points that are to be - * inserted before the current bezier curve. It - * can be seen as some sort of base location: - * Where would the bezier curve be (with - * respect to the current value of `order') if - * no additional control points had to be - * inserted? - * - * ii) `i * (amount - a)', which is the total - * number of control points to be inserted - * before the current bezier curve - * (`i * amount') taking into account the - * increasing order (`order' and `a' are - * increased equally, thus, `a' compensates for - * the increasing value of `order'). This part - * adds the necessary offset to the base - * location (`i * order'). */ - offset = (i * order + i * (amount - a)) * dim; - /* Duplicate last control point to the new end - * position (next control point). */ - memmove(ctrlp + offset + ((order)*dim), - ctrlp + offset + ((order - 1) * dim), - dim * sizeof(tsReal)); - /* All but the outer control points must be - * recalculated (domain: [1, order - 1]). By - * traversing backwards, control points can be - * modified in-place. */ - for (c = order - 1; c > 0; c--) { - /* Location of current control point - * within current bezier curve. */ - idx = offset + c * dim; - f = (tsReal)c / (tsReal)(order); - f_hat = 1 - f; - for (d = 0; d < dim; d++) { - /* For the sake of space, we - * increment idx by d and - * decrement it at the end of - * this loop. */ - idx += d; - ctrlp[idx] = - f * ctrlp[idx - dim] + - f_hat * ctrlp[idx]; - /* Reset idx. */ - idx -= d; - } - } - - /* ...2) Increase the multiplicity of the - * second knot group (maximum of the domain of - * the current bezier curve) by one. Note that - * this loop misses the last knot group (the - * group of the last bezier curve) as there is - * one more knot group than bezier curves to - * process. Thus, the last group must be - * increased separately after this loop. */ - - /* Location of current knot group. Each - * elevation (`a') inserts an additional - * knot into the knot vector of every bezier - * curve and increases the degree (`order') by - * one. The location is thus made up of two - * parts: - * - * i) `i * order', which is the location taking - * into account the increasing order but - * neglecting the knots that are to be inserted - * before the current knot group. It can be - * seen as some sort of base location: Where - * would the knot group be (with respect to the - * current value of `order') if no additional - * knots had to be inserted? - * - * ii) `i * (amount - a)', which is the total - * number of knots to be inserted before the - * current knot group (`i * amount') taking - * into account the increasing order (`order' - * and `a' are increased equally, thus, `a' - * compensates for the increasing value of - * `order'). This part adds the necessary - * offset to the base location - * (`i * order'). */ - offset = i * order + i * (amount - a); - /* Duplicate knot. */ - knots[offset + order] = knots[offset]; - } - - /* Increase the multiplicity of the very last knot - * group (the second group of the last bezier curve) - * by one. For more details, see knot duplication in - * previous loop. */ - offset = num_beziers * order + - num_beziers * (amount - a); - knots[offset + order] = knots[offset]; - - /* Elevated by one. */ - order++; - } - - /* Combine bezier curves. */ - d = 0; /* Number of removed knots/control points. */ - for (i = 0; i < num_beziers - 1; i++) { - /* Is the last control point of bezier curve `i' equal - * to the first control point of bezier curve `i+1'? */ - last = ctrlp + (i * order /* base location of `i' */ - - d /* minus the number of removed values */ - + (order - 1) /* jump to last control point */ - ) * dim; - first = last + dim; /* next control point */ - if (ts_distance(last, first, dim) <= epsilon) { - /* Move control points. */ - memmove(last, first, (num_beziers - 1 - i) * order * dim * sizeof(tsReal)); - - /* Move knots. `last' is the last knot of the - * second knot group of bezier curve `i'. - * `first' is the first knot of the first knot - * group of bezier curve `i+1'. The - * calculations are quite similar to those for - * the control points `last' and `first' (see - * above). */ - last = knots + i * order - d + (2 * order - 1); - first = last + 1; - memmove(last, first, (num_beziers - 1 - i) * order * sizeof(tsReal)); - - /* Removed one knot/control point. */ - d++; - } - } - - /* Repair internal state. */ - worker.pImpl->deg = order - 1; - worker.pImpl->n_knots -= d; - worker.pImpl->n_ctrlp = ts_bspline_num_knots(&worker) - order; - memmove(ts_int_bspline_access_knots(&worker), - knots, ts_bspline_sof_knots(&worker)); - worker.pImpl = realloc(worker.pImpl, - ts_int_bspline_sof_state(&worker)); - if (worker.pImpl == NULL) { - TS_THROW_0(try, err, status, TS_MALLOC, - "out of memory") - } - - /* Move `worker' to output parameter. */ - if (spline == elevated) - ts_bspline_free(elevated); - ts_bspline_move(&worker, elevated); - TS_FINALLY - ts_bspline_free(&worker); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_align(const tsBSpline *s1, - const tsBSpline *s2, - tsReal epsilon, - tsBSpline *s1_out, - tsBSpline *s2_out, - tsStatus *status) { - tsBSpline s1_worker, s2_worker, *smaller, *larger; - tsDeBoorNet net; /* the net of `smaller'. */ - size_t i, missing, remaining; - tsReal min, max, shift, nextKnot; - tsError err; - - INIT_OUT_BSPLINE(s1, s1_out) - INIT_OUT_BSPLINE(s2, s2_out) - s1_worker = ts_bspline_init(); - s2_worker = ts_bspline_init(); - smaller = larger = NULL; - TS_TRY(try, err, status) - /* Set up `s1_worker' and `s2_worker'. After this - * if-elseif-else-block, `s1_worker' and `s2_worker' have same - * degree. */ - if (ts_bspline_degree(s1) < ts_bspline_degree(s2)) { - TS_CALL(try, err, ts_bspline_elevate_degree(s1, ts_bspline_degree(s2) - ts_bspline_degree(s1), epsilon, &s1_worker, status)) - TS_CALL(try, err, ts_bspline_copy(s2, &s2_worker, status)) - } else if (ts_bspline_degree(s2) < ts_bspline_degree(s1)) { - TS_CALL(try, err, ts_bspline_elevate_degree(s2, ts_bspline_degree(s1) - ts_bspline_degree(s2), epsilon, &s2_worker, status)) - TS_CALL(try, err, ts_bspline_copy(s1, &s1_worker, status)) - } else { - TS_CALL(try, err, ts_bspline_copy(s1, &s1_worker, status)) - TS_CALL(try, err, ts_bspline_copy(s2, &s2_worker, status)) - } - - /* Set up `smaller', `larger', and `net'. */ - if (ts_bspline_num_knots(&s1_worker) < - ts_bspline_num_knots(&s2_worker)) { - smaller = &s1_worker; - larger = &s2_worker; - } else { - smaller = &s2_worker; - larger = &s1_worker; - } - TS_CALL(try, err, ts_int_deboornet_new(smaller, &net, status)) - - /* Insert knots into `smaller' until it has the same number of - * knots (and therefore the same number of control points) as - * `larger'. */ - ts_bspline_domain(smaller, &min, &max); - missing = remaining = ts_bspline_num_knots(larger) - - ts_bspline_num_knots(smaller); - shift = (tsReal)0.0; - if (missing > 0) - shift = ((tsReal)1.0 / missing) * (tsReal)0.5; - for (i = 0; remaining > 0; i++, remaining--) { - nextKnot = (max - min) * ((tsReal)i / missing) + min; - nextKnot += shift; - TS_CALL(try, err, ts_int_bspline_eval_woa(smaller, nextKnot, &net, status)) - while (!ts_deboornet_num_insertions(&net)) { - /* Linear exploration for next knot. */ - nextKnot += 5 * TS_KNOT_EPSILON; - if (nextKnot > max) { - TS_THROW_0(try, err, status, - TS_NO_RESULT, - "no more knots for insertion") - } - TS_CALL(try, err, ts_int_bspline_eval_woa(smaller, nextKnot, &net, status)) - } - TS_CALL(try, err, ts_int_bspline_insert_knot(smaller, &net, 1, smaller, status)) - } - - if (s1 == s1_out) - ts_bspline_free(s1_out); - if (s2 == s2_out) - ts_bspline_free(s2_out); - ts_bspline_move(&s1_worker, s1_out); - /* if `s1_out' == `s2_out', `s2_worker' must not be moved - * because otherwise the memory of `s1_worker' is leaked - * (`s2_worker' overrides `s1_worker'). */ - if (s1_out != s2_out) - ts_bspline_move(&s2_worker, s2_out); - TS_FINALLY - ts_bspline_free(&s1_worker); - ts_bspline_free(&s2_worker); - ts_deboornet_free(&net); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_morph(const tsBSpline *origin, - const tsBSpline *target, - tsReal t, - tsReal epsilon, - tsBSpline *out, - tsStatus *status) { - tsBSpline origin_al, target_al; /* aligned origin and target */ - tsReal *origin_al_c, *origin_al_k; /* control points and knots */ - tsReal *target_al_c, *target_al_k; /* control points and knots */ - - /* Properties of `out'. */ - size_t deg, dim, num_ctrlp, num_knots; - tsReal *ctrlp, *knots; - tsBSpline tmp; /* temporary buffer if `out' must be resized */ - - tsReal t_hat; - size_t i, offset, d; - tsError err; - - origin_al = ts_bspline_init(); - target_al = ts_bspline_init(); - TS_TRY(try, err, status) - /* Clamp `t' to domain [0, 1] and set up `t_hat'. */ - if (t < (tsReal)0.0) t = (tsReal)0.0; - if (t > (tsReal)1.0) t = (tsReal)1.0; - t_hat = (tsReal)1.0 - t; - - /* Set up `origin_al' and `target_al'. */ - /* Degree must be elevated... */ - if (ts_bspline_degree(origin) != ts_bspline_degree(target) || - /* .. or knots (and thus control points) must be inserted. */ - ts_bspline_num_knots(origin) != ts_bspline_num_knots(target)) { - TS_CALL(try, err, ts_bspline_align(origin, target, epsilon, &origin_al, &target_al, status)); - } else { - /* Flat copy. */ - origin_al = *origin; - target_al = *target; - } - origin_al_c = ts_int_bspline_access_ctrlp(&origin_al); - origin_al_k = ts_int_bspline_access_knots(&origin_al); - target_al_c = ts_int_bspline_access_ctrlp(&target_al); - target_al_k = ts_int_bspline_access_knots(&target_al); - - /* Set up `out'. */ - deg = ts_bspline_degree(&origin_al); - num_ctrlp = ts_bspline_num_control_points(&origin_al); - dim = ts_bspline_dimension(&origin_al); - if (ts_bspline_dimension(&target_al) < dim) - dim = ts_bspline_dimension(&target_al); - if (out->pImpl == NULL) { - TS_CALL(try, err, ts_bspline_new(num_ctrlp, dim, deg, TS_OPENED /* doesn't matter */, out, status)) - } else if (ts_bspline_degree(out) != deg || - ts_bspline_num_control_points(out) != num_ctrlp || - ts_bspline_dimension(out) != dim) { - TS_CALL(try, err, ts_bspline_new(num_ctrlp, dim, deg, TS_OPENED /* doesn't matter */, &tmp, status)) - ts_bspline_free(out); - ts_bspline_move(&tmp, out); - } - num_knots = ts_bspline_num_knots(out); - ctrlp = ts_int_bspline_access_ctrlp(out); - knots = ts_int_bspline_access_knots(out); - - /* Interpolate control points. */ - for (i = 0; i < num_ctrlp; i++) { - for (d = 0; d < dim; d++) { - offset = i * dim + d; - ctrlp[offset] = t * target_al_c[offset] + - t_hat * origin_al_c[offset]; - } - } - - /* Interpolate knots. */ - for (i = 0; i < num_knots; i++) { - knots[i] = t * target_al_k[i] + - t_hat * origin_al_k[i]; - } - TS_FINALLY - if (origin->pImpl != origin_al.pImpl) - ts_bspline_free(&origin_al); - if (target->pImpl != target_al.pImpl) - ts_bspline_free(&target_al); - TS_END_TRY_RETURN(err) -} -/*! @} */ - -/*! @name Serialization and Persistence - * - * @{ - */ -tsError -ts_int_bspline_to_json(const tsBSpline *spline, - JSON_Value **value, - tsStatus *status) { - const size_t deg = ts_bspline_degree(spline); - const size_t dim = ts_bspline_dimension(spline); - const size_t len_ctrlp = ts_bspline_len_control_points(spline); - const size_t len_knots = ts_bspline_num_knots(spline); - const tsReal *ctrlp = ts_int_bspline_access_ctrlp(spline); - const tsReal *knots = ts_int_bspline_access_knots(spline); - - size_t i; /**< Used in loops */ - tsError err; - - JSON_Value *ctrlp_value; - JSON_Value *knots_value; - JSON_Object *spline_object; - JSON_Array *ctrlp_array; - JSON_Array *knots_array; - - *value = ctrlp_value = knots_value = NULL; - TS_TRY(values, err, status) - /* Init memory. */ - *value = json_value_init_object(); - if (!*value) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - ctrlp_value = json_value_init_array(); - if (!ctrlp_value) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - knots_value = json_value_init_array(); - if (!knots_value) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - - /* Although the following functions cannot fail, that is, they - * won't return NULL or JSONFailure, we nevertheless handle - * unexpected return values. */ - - /* Init output. */ - spline_object = json_value_get_object(*value); - if (!spline_object) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - - /* Set degree and dimension. */ - if (JSONSuccess != json_object_set_number(spline_object, - "degree", - (double)deg)) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - if (JSONSuccess != json_object_set_number(spline_object, - "dimension", - (double)dim)) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - - /* Set control points. */ - if (JSONSuccess != json_object_set_value(spline_object, - "control_points", - ctrlp_value)) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - ctrlp_array = json_array(ctrlp_value); - if (!ctrlp_array) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - for (i = 0; i < len_ctrlp; i++) { - if (JSONSuccess != json_array_append_number( - ctrlp_array, (double)ctrlp[i])) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - } - - /* Set knots. */ - if (JSONSuccess != json_object_set_value(spline_object, - "knots", - knots_value)) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - knots_array = json_array(knots_value); - if (!knots_array) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - for (i = 0; i < len_knots; i++) { - if (JSONSuccess != json_array_append_number( - knots_array, (double)knots[i])) { - TS_THROW_0(values, err, status, TS_MALLOC, - "out of memory") - } - } - TS_CATCH(err) - if (*value) - json_value_free(*value); - if (ctrlp_value && !json_value_get_parent(ctrlp_value)) - json_value_free(ctrlp_value); - if (knots_value && !json_value_get_parent(knots_value)) - json_value_free(knots_value); - *value = NULL; - TS_END_TRY_RETURN(err) -} - -tsError -ts_int_bspline_parse_json(const JSON_Value *spline_value, - tsBSpline *spline, - tsStatus *status) { - size_t deg, dim, len_ctrlp, num_knots; - tsReal *ctrlp, *knots; - - JSON_Object *spline_object; - JSON_Value *deg_value; - JSON_Value *dim_value; - JSON_Value *ctrlp_value; - JSON_Array *ctrlp_array; - JSON_Value *knots_value; - JSON_Array *knots_array; - JSON_Value *real_value; - size_t i; - tsError err; - - ts_int_bspline_init(spline); - - /* Read spline object. */ - if (json_value_get_type(spline_value) != JSONObject) - TS_RETURN_0(status, TS_PARSE_ERROR, "invalid json input") - spline_object = json_value_get_object(spline_value); - if (!spline_object) - TS_RETURN_0(status, TS_PARSE_ERROR, "invalid json input") - - /* Read degree. */ - deg_value = json_object_get_value(spline_object, "degree"); - if (json_value_get_type(deg_value) != JSONNumber) - TS_RETURN_0(status, TS_PARSE_ERROR, "degree is not a number") - if (json_value_get_number(deg_value) < -0.01f) { - TS_RETURN_1(status, TS_PARSE_ERROR, - "degree (%f) < 0", - json_value_get_number(deg_value)) - } - deg = (size_t)json_value_get_number(deg_value); - - /* Read dimension. */ - dim_value = json_object_get_value(spline_object, "dimension"); - if (json_value_get_type(dim_value) != JSONNumber) { - TS_RETURN_0(status, TS_PARSE_ERROR, - "dimension is not a number") - } - if (json_value_get_number(dim_value) < 0.99f) { - TS_RETURN_1(status, TS_PARSE_ERROR, - "dimension (%f) < 1", - json_value_get_number(deg_value)) - } - dim = (size_t)json_value_get_number(dim_value); - - /* Read length of control point vector. */ - ctrlp_value = json_object_get_value(spline_object, "control_points"); - if (json_value_get_type(ctrlp_value) != JSONArray) { - TS_RETURN_0(status, TS_PARSE_ERROR, - "control_points is not an array") - } - ctrlp_array = json_value_get_array(ctrlp_value); - len_ctrlp = json_array_get_count(ctrlp_array); - if (len_ctrlp % dim != 0) { - TS_RETURN_2(status, TS_PARSE_ERROR, - "len(control_points) (%lu) %% dimension (%lu) != 0", - (unsigned long)len_ctrlp, (unsigned long)dim) - } - - /* Read number of knots. */ - knots_value = json_object_get_value(spline_object, "knots"); - if (json_value_get_type(knots_value) != JSONArray) { - TS_RETURN_0(status, TS_PARSE_ERROR, - "knots is not an array") - } - knots_array = json_value_get_array(knots_value); - num_knots = json_array_get_count(knots_array); - - /* Create spline. */ - TS_TRY(try, err, status) - TS_CALL(try, err, ts_bspline_new(len_ctrlp / dim, dim, deg, TS_CLAMPED, spline, status)) - if (num_knots != ts_bspline_num_knots(spline)) - TS_THROW_2(try, err, status, TS_NUM_KNOTS, - "unexpected num(knots): (%lu) != (%lu)", - (unsigned long)num_knots, - (unsigned long)ts_bspline_num_knots(spline)) - - /* Set control points. */ - ctrlp = ts_int_bspline_access_ctrlp(spline); - for (i = 0; i < len_ctrlp; i++) { - real_value = json_array_get_value(ctrlp_array, i); - if (json_value_get_type(real_value) != JSONNumber) - TS_THROW_1(try, err, status, TS_PARSE_ERROR, - "control_points: value at index %lu is not a number", - (unsigned long)i) - ctrlp[i] = (tsReal)json_value_get_number(real_value); - } - TS_CALL(try, err, ts_bspline_set_control_points(spline, ctrlp, status)) - - /* Set knots. */ - knots = ts_int_bspline_access_knots(spline); - for (i = 0; i < num_knots; i++) { - real_value = json_array_get_value(knots_array, i); - if (json_value_get_type(real_value) != JSONNumber) - TS_THROW_1(try, err, status, TS_PARSE_ERROR, - "knots: value at index %lu is not a number", - (unsigned long)i) - knots[i] = (tsReal)json_value_get_number(real_value); - } - TS_CALL(try, err, ts_bspline_set_knots(spline, knots, status)) - TS_CATCH(err) - ts_bspline_free(spline); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_to_json(const tsBSpline *spline, - char **json, - tsStatus *status) { - tsError err; - JSON_Value *value = NULL; - *json = NULL; - TS_CALL_ROE(err, ts_int_bspline_to_json(spline, &value, status)) - *json = json_serialize_to_string_pretty(value); - json_value_free(value); - if (!*json) - TS_RETURN_0(status, TS_MALLOC, "out of memory") - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_parse_json(const char *json, - tsBSpline *spline, - tsStatus *status) { - tsError err; - JSON_Value *value = NULL; - ts_int_bspline_init(spline); - TS_TRY(try, err, status) - value = json_parse_string(json); - if (!value) { - TS_RETURN_0(status, TS_PARSE_ERROR, - "invalid json input") - } - TS_CALL(try, err, ts_int_bspline_parse_json(value, spline, status)) - TS_FINALLY - if (value) - json_value_free(value); - TS_END_TRY_RETURN(err) -} - -tsError -ts_bspline_save(const tsBSpline *spline, - const char *path, - tsStatus *status) { - tsError err; - JSON_Status json_status; - JSON_Value *value = NULL; - TS_CALL_ROE(err, ts_int_bspline_to_json(spline, &value, status)) - json_status = json_serialize_to_file_pretty(value, path); - json_value_free(value); - if (json_status != JSONSuccess) - TS_RETURN_0(status, TS_IO_ERROR, "unexpected io error") - TS_RETURN_SUCCESS(status) -} - -tsError -ts_bspline_load(const char *path, - tsBSpline *spline, - tsStatus *status) { - tsError err; - FILE *file = NULL; - JSON_Value *value = NULL; - ts_int_bspline_init(spline); - TS_TRY(try, err, status) - file = fopen(path, "r"); - if (!file) { - TS_THROW_0(try, err, status, TS_IO_ERROR, - "unable to open file") - } - value = json_parse_file(path); - if (!value) { - TS_RETURN_0(status, TS_PARSE_ERROR, - "invalid json input") - } - TS_CALL(try, err, ts_int_bspline_parse_json(value, spline, status)) - TS_FINALLY - if (file) - fclose(file); - if (value) - json_value_free(value); - TS_CATCH(err) - ts_bspline_free(spline); - TS_END_TRY_RETURN(err) -} -/*! @} */ - -/*! @name Vector Math - * @{ - */ -void ts_vec2_init(tsReal *out, - tsReal x, - tsReal y) { - out[0] = x; - out[1] = y; -} - -void ts_vec3_init(tsReal *out, - tsReal x, - tsReal y, - tsReal z) { - out[0] = x; - out[1] = y; - out[2] = z; -} - -void ts_vec4_init(tsReal *out, - tsReal x, - tsReal y, - tsReal z, - tsReal w) { - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = w; -} - -void ts_vec2_set(tsReal *out, - const tsReal *x, - size_t dim) { - const size_t n = dim > 2 ? 2 : dim; - memmove(out, x, n * sizeof(tsReal)); - if (dim < 2) - ts_arr_fill(out + dim, 2 - dim, (tsReal)0.0); -} - -void ts_vec3_set(tsReal *out, - const tsReal *x, - size_t dim) { - const size_t n = dim > 3 ? 3 : dim; - memmove(out, x, n * sizeof(tsReal)); - if (dim < 3) - ts_arr_fill(out + dim, 3 - dim, (tsReal)0.0); -} - -void ts_vec4_set(tsReal *out, - const tsReal *x, - size_t dim) { - const size_t n = dim > 4 ? 4 : dim; - memmove(out, x, n * sizeof(tsReal)); - if (dim < 4) - ts_arr_fill(out + dim, 4 - dim, (tsReal)0.0); -} - -void ts_vec_add(const tsReal *x, - const tsReal *y, - size_t dim, - tsReal *out) { - size_t i; - for (i = 0; i < dim; i++) - out[i] = x[i] + y[i]; -} - -void ts_vec_sub(const tsReal *x, - const tsReal *y, - size_t dim, - tsReal *out) { - size_t i; - if (x == y) { - /* More stable version. */ - ts_arr_fill(out, dim, (tsReal)0.0); - return; - } - for (i = 0; i < dim; i++) - out[i] = x[i] - y[i]; -} - -tsReal -ts_vec_dot(const tsReal *x, - const tsReal *y, - size_t dim) { - size_t i; - tsReal dot = 0; - for (i = 0; i < dim; i++) - dot += x[i] * y[i]; - return dot; -} - -tsReal -ts_vec_angle(const tsReal *x, - const tsReal *y, - tsReal *buf, - size_t dim) { - const tsReal *x_norm, *y_norm; - if (buf) { - ts_vec_norm(x, dim, buf); - ts_vec_norm(y, dim, buf + dim); - x_norm = buf; - y_norm = buf + dim; - } else { - x_norm = x; - y_norm = y; - } - return (tsReal)( - /* Use doubles as long as possible. */ - acos(ts_vec_dot(x_norm, - y_norm, - dim)) * - (180.0 / TS_PI) /* radiant to degree */ - ); -} - -void ts_vec3_cross(const tsReal *x, - const tsReal *y, - tsReal *out) { - tsReal a, b, c; - a = x[1] * y[2] - x[2] * y[1]; - b = x[2] * y[0] - x[0] * y[2]; - c = x[0] * y[1] - x[1] * y[0]; - out[0] = a; - out[1] = b; - out[2] = c; -} - -void ts_vec_norm(const tsReal *x, - size_t dim, - tsReal *out) { - size_t i; - const tsReal m = ts_vec_mag(x, dim); - if (m < TS_LENGTH_ZERO) { - ts_arr_fill(out, dim, (tsReal)0.0); - return; - } - for (i = 0; i < dim; i++) - out[i] = x[i] / m; -} - -tsReal -ts_vec_mag(const tsReal *x, - size_t dim) { - size_t i; - tsReal sum = 0; - for (i = 0; i < dim; i++) - sum += (x[i] * x[i]); - return (tsReal)sqrt(sum); -} - -void ts_vec_mul(const tsReal *x, - size_t dim, - tsReal val, - tsReal *out) { - size_t i; - for (i = 0; i < dim; i++) - out[i] = x[i] * val; -} -/*! @} */ - -/*! @name Utility Functions - * - * @{ - */ -int ts_knots_equal(tsReal x, - tsReal y) { - return fabs(x - y) < TS_KNOT_EPSILON ? 1 : 0; -} - -void ts_arr_fill(tsReal *arr, - size_t num, - tsReal val) { - size_t i; - for (i = 0; i < num; i++) - arr[i] = val; -} - -tsReal ts_distance(const tsReal *x, - const tsReal *y, - size_t dim) { - size_t i; - tsReal sum = 0; - for (i = 0; i < dim; i++) - sum += (x[i] - y[i]) * (x[i] - y[i]); - return (tsReal)sqrt(sum); -} -/*! @} */ - -#ifdef _MSC_VER -#pragma warning(pop) -#endif diff --git a/source/engine/tinyspline.h b/source/engine/tinyspline.h deleted file mode 100644 index 44c94ea..0000000 --- a/source/engine/tinyspline.h +++ /dev/null @@ -1,2886 +0,0 @@ -/** @file */ - -#ifndef TINYSPLINE_H -#define TINYSPLINE_H - -#include - - - -/*! @name Deprecation - * - * The macro \c TS_DEPRECATED can be used to mark functions as - * deprecated. - * - * @{ - */ -#if defined(__GNUC__) || defined(__clang__) -#define TS_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define TS_DEPRECATED __declspec(deprecated) -#elif defined(SWIG) -#define TS_DEPRECATED -#else -#warning "WARNING: TS_DEPRECATED is not supported by compiler" -#define TS_DEPRECATED -#endif -/*! @} */ - - - -/*! @name Library Export/Import - * - * If TinySpline is built for Windows, the macros \c TINYSPLINE_SHARED_EXPORT - * and \c TINYSPLINE_SHARED_IMPORT define the Microsoft specific directives \c - * __declspec(dllexport) and \c __declspec(dllimport), respectively. More - * information on these directives can be found at: - * - * https://docs.microsoft.com/en-us/cpp/cpp/dllexport-dllimport - * - * If TinySpline is built to the ELF (most Unix like environments) or Mach (OS - * X) object format, \c TINYSPLINE_SHARED_EXPORT defines the directive - * __attribute__ ((visibility ("default"))) which, in combination with - * \c -fvisibility=hidden, behaves similar to \c __declspec(dllexport). \c - * TINYSPLINE_SHARED_IMPORT is set empty (i.e., it defines nothing). - * - * If none of the above applies, \c TINYSPLINE_SHARED_EXPORT and \c - * TINYSPLINE_SHARED_IMPORT are set empty (i.e., they define nothing). - * - * Depending on whether TinySpline is compiled as shared library or - * linked against as shared library, \c TINYSPLINE_API points to \c - * TINYSPLINE_SHARED_EXPORT (compiled) or \c TINYSPLINE_SHARED_IMPORT (linked - * against). All elements of TinySpline that needs to be exported/imported are - * annotated with \c TINYSPLINE_API. This eliminates the need for a - * module-definition (.def) file. If TinySpline is compiled or linked against - * as static library, \c TINYSPLINE_API is set empty (i.e., it defines - * nothing). - * - * If you consume TinySpline as shared library built for Windows, all you need - * is to define \c TINYSPLINE_SHARED. This will automatically import all - * required symbols. When compiling TinySpline, the build system should set all - * necessary defines. - * - * @{ - */ -#if defined(_WIN32) || defined(__CYGWIN__) -#define TINYSPLINE_SHARED_EXPORT __declspec(dllexport) -#define TINYSPLINE_SHARED_IMPORT __declspec(dllimport) -#elif defined(__ELF__) || defined(__MACH__) -#define TINYSPLINE_SHARED_EXPORT __attribute__ ((visibility ("default"))) -#define TINYSPLINE_SHARED_IMPORT -#else -#define TINYSPLINE_SHARED_EXPORT -#define TINYSPLINE_SHARED_IMPORT -#endif - -#ifdef TINYSPLINE_SHARED -#ifdef TINYSPLINE_EXPORT -#define TINYSPLINE_API TINYSPLINE_SHARED_EXPORT -#else -#define TINYSPLINE_API TINYSPLINE_SHARED_IMPORT -#endif -#else -#define TINYSPLINE_API -#endif - -#ifdef __cplusplus -extern "C" { -#endif -/*! @} */ - - - -/*! @name Predefined Constants - * - * The following constants have been adjusted to maintain internal consistency - * and should only be changed with great caution! The values chosen should be - * suitable for most environments and can be used with float (single) and - * double precision (see ::tsReal). If changes are necessary, please read the - * documentation of the constants in advance. - * - * @{ - */ -/** - * The mathematical constant pi. - */ -#define TS_PI 3.14159265358979323846 - -/** - * The maximum number of knots a spline can have. This constant is strongly - * related to ::TS_KNOT_EPSILON in that the larger ::TS_MAX_NUM_KNOTS is, the - * less precise ::TS_KNOT_EPSILON has to be (i.e., knots with greater distance - * are considered equal). Likewise, the more precise ::TS_KNOT_EPSILON is - * (i.e., knots with smaller distance are considered equal), the less - * ::TS_MAX_NUM_KNOTS has to be. By default, the relation between - * ::TS_MAX_NUM_KNOTS and ::TS_KNOT_EPSILON is as follows: - * - * TS_MAX_NUM_KNOTS = 1 / TS_KNOTS_EPSILON - */ -#define TS_MAX_NUM_KNOTS 10000 - -/** - * The minimum of the domain of newly created splines. Must be less than - * ::TS_DOMAIN_DEFAULT_MAX. This constant is used only when creating new - * splines. After creation, the domain of a spline can be adjusted as needed. - */ -#define TS_DOMAIN_DEFAULT_MIN 0.0f - -/** - * The maximum of the domain of newly created splines. Must be greater than - * ::TS_DOMAIN_DEFAULT_MIN. This constant is used only when creating new - * splines. After creation, the domain of a spline can be adjusted as needed. - */ -#define TS_DOMAIN_DEFAULT_MAX 1.0f - -/** - * If the distance between two knots falls below this threshold, they are - * considered equal. Must be positive ( > 0 ). This constant is strongly - * related to ::TS_MAX_NUM_KNOTS in that the more precise ::TS_KNOT_EPSILON is - * (i.e., knots with smaller distance are considered equal), the less - * ::TS_MAX_NUM_KNOTS has to be. Likewise, the larger ::TS_MAX_NUM_KNOTS is, - * the less precise ::TS_KNOT_EPSILON has to be (i.e., knots with greater - * distance are considered equal). By default, the relation between - * ::TS_KNOT_EPSILON and ::TS_MAX_NUM_KNOTS is as follows: - * - * TS_KNOT_EPSILON = 1 / TS_MAX_NUM_KNOTS - * - * It is recommended that ::TS_KNOT_EPSILON is aligned to the span of - * ::TS_DOMAIN_DEFAULT_MIN and ::TS_DOMAIN_DEFAULT_MAX. That is, adjacent - * floating point values in the domain [::TS_DOMAIN_DEFAULT_MIN, - * ::TS_DOMAIN_DEFAULT_MAX] should not be equal according to - * ::TS_KNOT_EPSILON. This is in particular recommended when ::TS_KNOT_EPSILON - * and ::TS_MAX_NUM_KNOTS are related to each other as described above. - */ -#define TS_KNOT_EPSILON 1e-4f - -/** - * If the distance between two (control) points is less than or equal to this - * threshold, they are considered equal. This constant is not used directly by - * the C interface. Rather, it serves as a viable default value for functions - * requiring an epsilon environment to decide whether two (control) points are - * equal or not. The C++ interface, for example, uses this as default value for - * optional parameters. - */ -#ifdef TINYSPLINE_FLOAT_PRECISION -#define TS_POINT_EPSILON 1e-3f -#else -#define TS_POINT_EPSILON 1e-5f -#endif - -/** - * If the length of an element (e.g., a vector) is less than this threshold, - * the length is considered \c 0. Must be positive ( > 0 ). - */ -#ifdef TINYSPLINE_FLOAT_PRECISION -#define TS_LENGTH_ZERO 1e-3f -#else -#define TS_LENGTH_ZERO 1e-4f -#endif -/*! @} */ - - - -/*! @name API Configuration - * - * In the following section, different aspects of TinySpline's API can be - * configured (compile-time). It is recommended to configure the API by - * supplying the corresponding preprocessor definition(s). That said, there is - * nothing wrong with editing the source code directly. - * - * @{ - */ -/** - * TinySpline uses its own typedef for floating point numbers. Supported are - * floats (single precision) and doubles (double precision). By default, - * doubles are used. Note that this typedef affects the entire API (i.e., types - * are not mixed; all structs and functions rely only on tsReal). Float - * precision is primarily used in combination with GLUT because GLUT's API - * doesn't support doubles: - * - * https://www.glprogramming.com/red/chapter12.html - * - * Generally, double precision is the right choice. Floats are mainly supported - * for legacy reasons. Yet, floats are not considered deprecated! If necessary, - * tsReal can also be typedefed to any other floating point representation. In - * this case, make sure to adjust TS_MAX_NUM_KNOTS and TS_KNOT_EPSILON - * (cf. Section "Predefined Constants"). - */ -#ifdef TINYSPLINE_FLOAT_PRECISION -typedef float tsReal; -#else -typedef double tsReal; -#endif -/*! @} */ - - - -/*! @name Error Handling - * - * There are three types of error handling in TinySpline. - * - * 1. Return value: Functions that can fail return a special error code - * (::tsError). If the error code is not \c 0 (::TS_SUCCESS), an error occurred - * during execution. For example: - * - * if ( ts_bspline_to_beziers(&spline, &beziers, NULL) ) { - * ... An error occurred ... - * } - * - * It is of course possible to check the actual type of error: - * - * tsError error = ts_bspline_to_beziers(&spline, &beziers, NULL); - * if (error == TS_MALLOC) { - * ... Out of memory ... - * } else if (error == ... - * - * This type of error handling is used in many C programs. The disadvantage - * is that there is no additional error message besides the error code (with - * which the cause of an error could be specified in more detail). Some - * libraries make do with global variables in which error messages are stored - * for later purpose (e.g., \a errno and \a strerror). Unfortunately, - * however, this approach (by design) is often not thread-safe. The second - * error handling option solves this issue. - * - * 2. ::tsStatus objects: Functions that can fail do not only return an error - * code, but also take a pointer to a ::tsStatus object as an optional - * parameter. In the event of an error, and if the supplied pointer is not - * NULL, the error message is stored in tsStatus#message and can be accessed by - * the caller. Using a ::tsStatus object, the example given in 1. can be - * modified as follows: - * - * tsStatus status; - * if ( ts_bspline_to_beziers(&spline, &beziers, &status) ) { - * status.code; // error code - * status.message; // error message - * } - * - * Note that ::tsStatus objects can be reused: - * - * tsStatus status; - * if ( ts_bspline_to_beziers(&spline, &beziers, &status) ) { - * ... - * } - * ... - * if ( ts_bspline_derive(&beziers, 1, 0.001, &beziers, &status) ) { - * ... - * } - * - * If you would like to use this type of error handling in your own functions - * (in particular the optional ::tsStatus parameter), you may wonder whether - * there is an easy way to return error codes and format error messages. This - * is where the macros ::TS_RETURN_0 -- ::TS_RETURN_4 come into play. They - * can, for example, be used as follows: - * - * tsError my_function(..., tsStatus *status, ...) - * { - * ... - * tsReal *points = (tsReal *) malloc(len * sizeof(tsReal)); - * if (!points) - * TS_RETURN_0(status, TS_MALLOC, "out of memory") - * ... - * } - * - * The \c N in \c TS_RETURN_ denotes the number of format specifier in the - * supplied format string (cf. sprintf(char *, const char *, ... )). - * - * 3. Try-catch-finally blocks: TinySpline provides a set of macros that can be - * used when a complex control flow is necessary. The macros create a structure - * that is similar to the exception handling mechanisms of high-level languages - * such as C++. The basic structure is as follows: - * - * TS_TRY(try, error, status) // `status' may be NULL - * ... - * TS_CALL( try, error, ts_bspline_to_beziers( - * &spline, &beziers, status) ) - * ... - * TS_CATCH(error) - * ... Executed in case of an error ... - * ... `error' (tsError) indicates the type of error. - * ... `status' (tsStatus) contains the error code and message ... - * TS_FINALLY - * ... Executed in any case ... - * TS_END_TRY - * - * ::TS_TRY and ::TS_END_TRY mark the boundaries of a try-catch-finally - * block. Every block has an identifier (name) that must be unique within a - * scope. The name of a block is set via the first argument of ::TS_TRY (\c - * try in the example listed above). The control flow of a try-catch-finally - * block is directed via the second and third argument of ::TS_TRY (\c error - * and \c status in the example listed above) and the utility macro - * ::TS_CALL. The second argument of ::TS_TRY, a ::tsError, is mandatory. The - * third argument of ::TS_TRY, a ::tsStatus object, is optional, that is, it - * may be \c NULL. ::TS_CALL serves as a wrapper for functions with return - * type ::tsError. If the called functions fails (more on that later), - * ::TS_CALL immediately jumps into the ::TS_CATCH section where \c error and - * \c status can be evaluated as needed (note that \c status may be \c - * NULL). The ::TS_FINALLY section is executed in any case and is in - * particularly helpful when resources (such as heap-memory, file-handles - * etc.) must be released. - * - * While ::TS_CALL can be used to wrap functions with return type ::tsError, - * sooner or later it will be necessary to delegate the failure of other - * kinds of functions (i.e., functions outside of TinySpline; e.g., - * malloc(size_t)). This is the purpose of the ::TS_THROW_0 -- ::TS_THROW_4 - * macros. It is not by coincidence that the signature of the \c TS_THROW_ - * macros is quite similar to that of the \c TS_RETURN_ macros. Both - * "macro groups" are used to report errors. The difference between \c - * TS_RETURN_ and TS_THROW_, however, is that the former exits a - * function (i.e., a \c return statement is inserted by these macros) while - * the latter jumps into a catch block (the catch block to jump into is set - * via the first argument of \c TS_THROW_): - * - * tsBSpline spline = ts_bspline_init(); - * tsReal *points = NULL; - * TS_TRY(try, error, status) - * ... - * tsReal *points = (tsReal *) malloc(len * sizeof(tsReal)); - * if (!points) - * TS_THROW_0(try, status, TS_MALLOC, "out of memory") - * ... - * TS_CALL( try, error, ts_bspline_interpolate_cubic_natural( - * points, len / dim, dim, &spline, status) ) - * ... - * TS_CATCH(error) - * ... Log error message ... - * TS_FINALLY - * ts_bspline_free(&spline); - * if (points) - * free(points); - * TS_END_TRY - * - * In all likelihood, you are already familiar with this kind error - * handling. Actually, there are a plethora of examples available online - * showing how exception-like error handling can be implemented in C. What - * most of these examples have in common is that they suggest to wrap the - * functions \c setjmp and \c longjmp (see setjmp.h) with macros. While this - * undoubtedly is a clever trick, \c setjmp and \c longjmp have no viable - * (i.e, thread-safe) solution for propagating the cause of an error (in the - * form of a human-readable error message) back to the client of a - * library. Therefore, TinySpline implements try-catch-finally blocks with \c - * if statements, labels, and \c goto statements (TS_THROW_). - * - * ::TS_TRY is flexible enough to be used in functions that are in turn - * embedded into TinySpline's error handling system: - * - * tsError my_function(..., tsStatus *status, ...) - * { - * tsError error; - * TS_TRY(try, error, status) - * ... - * TS_END_TRY - * return error; - * } - * - * as well as functions forming the root of a call stack that uses - * TinySpline's error handling system: - * - * tsStatus status; - * TS_TRY(try, status.code, &status) - * ... - * TS_END_TRY - * - * There is some utility macros that might be useful when dealing with - * try-catch-finally blocks: - * - * - ::TS_END_TRY_RETURN: Returns the supplied error code immediately after - * completing the corresponding block. Can be used as follows: - * - * tsError my_function(..., tsStatus *status, ...) - * { - * tsError error; - * TS_TRY(try, error, status) - * ... - * TS_END_TRY_RETURN(error) - * } - * - * - ::TS_END_TRY_ROE: Like ::TS_END_TRY_RETURN but returns the supplied - * error code, \c e, if \c e is not ::TS_SUCCESS (\c ROE means - * Return On Error). Can be used as follows: - * - * tsError my_function(..., tsStatus *status, ...) - * { - * tsError error; - * TS_TRY(try, error, status) - * ... - * TS_END_TRY_ROE(error) - * ... Additional code. The code is executed only if `error' is - * TS_SUCCESS, that is, if no error occurred in the try block - * above ... - * } - * - * - ::TS_CALL_ROE: Calls the supplied function and returns its error code, - * \c e, if \c e is not ::TS_SUCCESS. This macro can be seen as a \e - * minified try block (a dedicated try block is not needed). - * - * - ::TS_RETURN_SUCCESS: Shortcut for ::TS_RETURN_0 with error code - * ::TS_SUCCESS and an empty error message. - * - * @{ - */ -/** - * Defines different error codes. - */ -typedef enum -{ - /** No error. */ - TS_SUCCESS = 0, - - /** Memory cannot be allocated (malloc, realloc etc.). */ - TS_MALLOC = -1, - - /** Points have dimensionality 0. */ - TS_DIM_ZERO = -2, - - /** degree >= num(control_points). */ - TS_DEG_GE_NCTRLP = -3, - - /** Knot is not within the domain. */ - TS_U_UNDEFINED = -4, - - /** multiplicity(knot) > order */ - TS_MULTIPLICITY = -5, - - /** Decreasing knot vector. */ - TS_KNOTS_DECR = -6, - - /** Unexpected number of knots. */ - TS_NUM_KNOTS = -7, - - /** Spline is not derivable. */ - TS_UNDERIVABLE = -8, - - /** len(control_points) % dimension != 0. */ - TS_LCTRLP_DIM_MISMATCH = -10, - - /** Error while reading/writing a file. */ - TS_IO_ERROR = -11, - - /** Error while parsing a serialized entity. */ - TS_PARSE_ERROR = -12, - - /** Index does not exist (e.g., when accessing an array). */ - TS_INDEX_ERROR = -13, - - /** Function returns without result (e.g., approximations). */ - TS_NO_RESULT = -14, - - /** Unexpected number of points. */ - TS_NUM_POINTS = -15 -} tsError; - -/** - * Stores an error code (see ::tsError) with corresponding message. - */ -typedef struct -{ - /** The error code. */ - tsError code; - - /** - * The corresponding error message (encoded as C string). Memory is - * allocated on stack so as to be able to provide a meaningful message - * in the event of memory issues (cf. ::TS_MALLOC). - */ - char message[100]; -} tsStatus; - -#define TS_TRY(label, error, status) \ -{ \ - (error) = TS_SUCCESS; \ - if ((status) != NULL) { \ - (status)->code = TS_SUCCESS; \ - (status)->message[0] = '\0'; \ - } \ - __ ## label ## __: \ - if (!(error)) { - -#define TS_CALL(label, error, call) \ - (error) = (call); \ - if ((error)) goto __ ## label ## __; - -#define TS_CATCH(error) \ - } if ((error)) { - -#define TS_FINALLY \ - } { - -#define TS_END_TRY \ - } \ -} - -#define TS_END_TRY_RETURN(error) \ - TS_END_TRY return (error); - -#define TS_END_TRY_ROE(error) \ - TS_END_TRY if ((error)) return error; - -#define TS_CALL_ROE(error, call) \ -{ \ - (error) = (call); \ - if ((error)) return error; \ -} - -#define TS_RETURN_SUCCESS(status) \ -{ \ - if ((status) != NULL) { \ - (status)->code = TS_SUCCESS; \ - (status)->message[0] = '\0'; \ - } \ - return TS_SUCCESS; \ -} - -#define TS_RETURN_0(status, error, msg) \ -{ \ - if ((status) != NULL) { \ - (status)->code = error; \ - sprintf((status)->message, msg); \ - } \ - return error; \ -} - -#define TS_RETURN_1(status, error, msg, arg1) \ -{ \ - if ((status) != NULL) { \ - (status)->code = error; \ - sprintf((status)->message, msg, arg1); \ - } \ - return error; \ -} - -#define TS_RETURN_2(status, error, msg, arg1, arg2) \ -{ \ - if ((status) != NULL) { \ - (status)->code = error; \ - sprintf((status)->message, msg, arg1, arg2); \ - } \ - return error; \ -} - -#define TS_RETURN_3(status, error, msg, arg1, arg2, arg3) \ -{ \ - if ((status) != NULL) { \ - (status)->code = error; \ - sprintf((status)->message, msg, arg1, arg2, arg3); \ - } \ - return error; \ -} - -#define TS_RETURN_4(status, error, msg, arg1, arg2, arg3, arg4) \ -{ \ - if ((status) != NULL) { \ - (status)->code = error; \ - sprintf((status)->message, msg, arg1, arg2, arg3, arg4); \ - } \ - return error; \ -} - -#define TS_THROW_0(label, error, status, val, msg) \ -{ \ - (error) = val; \ - if ((status) != NULL) { \ - (status)->code = val; \ - sprintf((status)->message, msg); \ - } \ - goto __ ## label ## __; \ -} - -#define TS_THROW_1(label, error, status, val, msg, arg1) \ -{ \ - (error) = val; \ - if ((status) != NULL) { \ - (status)->code = val; \ - sprintf((status)->message, msg, arg1); \ - } \ - goto __ ## label ## __; \ -} - -#define TS_THROW_2(label, error, status, val, msg, arg1, arg2) \ -{ \ - (error) = val; \ - if ((status) != NULL) { \ - (status)->code = val; \ - sprintf((status)->message, msg, arg1, arg2); \ - } \ - goto __ ## label ## __; \ -} - -#define TS_THROW_3(label, error, status, val, msg, arg1, arg2, arg3) \ -{ \ - (error) = val; \ - if ((status) != NULL) { \ - (status)->code = val; \ - sprintf((status)->message, msg, arg1, arg2, arg3); \ - } \ - goto __ ## label ## __; \ -} - -#define TS_THROW_4(label, error, status, val, msg, arg1, arg2, arg3, arg4) \ -{ \ - (error) = val; \ - if ((status) != NULL) { \ - (status)->code = val; \ - sprintf((status)->message, msg, arg1, arg2, arg3, arg4); \ - } \ - goto __ ## label ## __; \ -} -/*! @} */ - - - -/*! @name Utility Structs and Enums - * - * @{ - */ -/** - * Describes the structure of the knot vector. More details can be found at: - * - * www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html - */ -typedef enum -{ - /** Uniformly spaced knot vector with opened end knots. */ - TS_OPENED = 0, - - /** Uniformly spaced knot vector with clamped end knots. */ - TS_CLAMPED = 1, - - /** - * Uniformly spaced knot vector where the multiplicity of each knot is - * equal to the order of the spline. - */ - TS_BEZIERS = 2 -} tsBSplineType; - -/** - * A three-dimensional TNB-vector with position. More details can be found at: - * - * https://en.wikipedia.org/wiki/Frenet-Serret_formulas - * https://www.math.tamu.edu/~tkiffe/calc3/tnb/tnb.html - * - * TNB stands for \e tangent, \e normal, and \e binormal. - */ -typedef struct -{ - /** Position of the TNB-vector. */ - tsReal position[3]; - - /** Tangent of the TNB-vector. */ - tsReal tangent[3]; - - /** Normal of the TNB-vector. */ - tsReal normal[3]; - - /** Binormal of the TNB-vector. */ - tsReal binormal[3]; -} tsFrame; -/*! @} */ - - - -/*! @name B-Spline Data - * - * The internal state of ::tsBSpline is protected using the PIMPL design - * pattern (see https://en.cppreference.com/w/cpp/language/pimpl for more - * details). The data of an instance can be accessed with the functions listed - * in this section. - * - * @{ - */ -/** - * Represents a B-Spline, which may also be used for NURBS, Bezier curves, - * lines, and points. NURBS use homogeneous coordinates to store their control - * points (i.e. the last component of a control point stores the weight). - * Bezier curves are B-Splines with 'num_control_points == order' and a - * clamped knot vector, which lets them pass through their first and last - * control point (a property which does not necessarily apply to B-Splines and - * NURBS). Lines and points, on that basis, are Bezier curves of degree 1 - * (lines) and 0 (points). - * - * Two dimensional control points are stored as follows: - * - * [x_0, y_0, x_1, y_1, ..., x_n-1, y_n-1] - * - * Tree dimensional control points are stored as follows: - * - * [x_0, y_0, z_0, x_1, y_1, z_1, ..., x_n-1, y_n-1, z_n-1] - * - * ... and so on. As already mentioned, NURBS use homogeneous coordinates to - * store their control points. For example, a NURBS in 2D stores its control - * points as follows: - * - * [x_0*w_0, y_0*w_0, w_0, x_1*w_1, y_1*w_1, w_1, ...] - * - * where 'w_i' is the weight of the i'th control point. - */ -typedef struct -{ - struct tsBSplineImpl *pImpl; /**< The actual implementation. */ -} tsBSpline; - -/** - * Returns the degree of \p spline. - * - * @param[in] spline - * The spline whose degree is read. - * @return - * The degree of \p spline. - */ -size_t TINYSPLINE_API -ts_bspline_degree(const tsBSpline *spline); - -/** - * Returns the order (degree + 1) of \p spline. - * - * @param[in] spline - * The spline whose order is read. - * @return - * The order of \p spline. - */ -size_t TINYSPLINE_API -ts_bspline_order(const tsBSpline *spline); - -/** - * Returns the dimensionality of \p spline, that is, the number of components - * of its control points (::ts_bspline_control_points). One-dimensional splines - * are possible, albeit their benefit might be questionable. - * - * @param[in] spline - * The spline whose dimension is read. - * @return - * The dimension of \p spline (>= 1). - */ -size_t TINYSPLINE_API -ts_bspline_dimension(const tsBSpline *spline); - -/** - * Returns the length of the control point array of \p spline. - * - * @param[in] spline - * The spline whose length of the control point array is read. - * @return - * The length of the control point array of \p spline. - */ -size_t TINYSPLINE_API -ts_bspline_len_control_points(const tsBSpline *spline); - -/** - * Returns the number of control points of \p spline. - * - * @param[in] spline - * The spline whose number of control points is read. - * @return - * The number of control points of \p spline. - */ -size_t TINYSPLINE_API -ts_bspline_num_control_points(const tsBSpline *spline); - -/** - * Returns the size of the control point array of \p spline. This function may - * be useful when copying control points using \e memcpy or \e memmove. - * - * @param[in] spline - * The spline whose size of the control point array is read. - * @return - * The size of the control point array of \p spline. - */ -size_t TINYSPLINE_API -ts_bspline_sof_control_points(const tsBSpline *spline); - -/** - * Returns the pointer to the control point array of \p spline. Note that the - * return type of this function is \c const for a reason. Clients should only - * read the returned array. When suppressing the constness and writing to the - * array against better knowledge, the client is on its own with regard to the - * consistency of the internal state of \p spline. If the control points of a - * spline need to be changed, use ::ts_bspline_control_points to obtain a copy - * of the control point array and ::ts_bspline_set_control_points to copy the - * changed values back to the spline. - * - * @param[in] spline - * The spline whose pointer to the control point array is returned. - * @return - * Pointer to the control point array of \p spline. - */ -const tsReal TINYSPLINE_API * -ts_bspline_control_points_ptr(const tsBSpline *spline); - -/** - * Returns a deep copy of the control points of \p spline. - * - * @param[in] spline - * The spline whose control points are read. - * @param[out] ctrlp - * The output array. \b Note: It is the responsibility of the client to - * release the allocated memory after use. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_control_points(const tsBSpline *spline, - tsReal **ctrlp, - tsStatus *status); - -/** - * Returns the pointer to the control point of \p spline at \p index. Note that - * the type of the out parameter \p ctrlp is \c const for a reason. Clients - * should only read the returned array. When suppressing the constness of \p - * ctrlp and writing to the array against better knowledge, the client is on - * its own with regard to the consistency of the internal state of \p - * spline. If one of the control points of a spline needs to be changed, use - * ::ts_bspline_set_control_points to copy the new control point to the spline. - * - * @param[in] spline - * The spline whose pointer to the control point at \p index is returned. - * @param[in] index - * Zero-based index of the control point to be returned. - * @param[out] ctrlp - * Pointer to the control point of \p spline at \p index. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_INDEX_ERROR - * If \p index is out of range. - */ -tsError TINYSPLINE_API -ts_bspline_control_point_at_ptr(const tsBSpline *spline, - size_t index, - const tsReal **ctrlp, - tsStatus *status); - -/** - * Sets the control points of \p spline. Creates a deep copy of \p ctrlp. - * - * @pre - * \p ctrlp has length ::ts_bspline_len_control_points. - * @param[out] spline - * The spline whose control points are set. - * @param[in] ctrlp - * The value. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - */ -tsError TINYSPLINE_API -ts_bspline_set_control_points(tsBSpline *spline, - const tsReal *ctrlp, - tsStatus *status); - -/** - * Sets the control point of \p spline at \p index. Creates a deep copy of - * \p ctrlp. - * - * @pre - * \p ctrlp has length ::ts_bspline_dimension. - * @param[out] spline - * The spline whose control point is set at \p index. - * @param[in] index - * Zero-based index of the control point to be set. - * @param[in] ctrlp - * The value. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_INDEX_ERROR - * If \p index is out of range. - */ -tsError TINYSPLINE_API -ts_bspline_set_control_point_at(tsBSpline *spline, - size_t index, - const tsReal *ctrlp, - tsStatus *status); - -/** - * Returns the number of knots of \p spline. - * - * @param[in] spline - * The spline whose number of knots is read. - * @return - * The number of knots of \p spline. - */ -size_t TINYSPLINE_API -ts_bspline_num_knots(const tsBSpline *spline); - -/** - * Returns the size of the knot array of \p spline. This function may be useful - * when copying knots using \e memcpy or \e memmove. - * - * @param[in] spline - * The spline whose size of the knot array is read. - * @return TS_SUCCESS - * The size of the knot array of \p spline. - */ -size_t TINYSPLINE_API -ts_bspline_sof_knots(const tsBSpline *spline); - -/** - * Returns the pointer to the knot vector of \p spline. Note that the return - * type of this function is \c const for a reason. Clients should only read the - * returned array. When suppressing the constness and writing to the array - * against better knowledge, the client is on its own with regard to the - * consistency of the internal state of \p spline. If the knot vector of a - * spline needs to be changed, use ::ts_bspline_knots to obtain a copy of the - * knot vector and ::ts_bspline_set_knots to copy the changed values back to - * the spline. - * - * @param[in] spline - * The spline whose pointer to the knot vector is returned. - * @return - * Pointer to the knot vector of \p spline. - */ -const tsReal TINYSPLINE_API * -ts_bspline_knots_ptr(const tsBSpline *spline); - -/** - * Returns a deep copy of the knots of \p spline. - * - * @param[in] spline - * The spline whose knots are read. - * @param[out] knots - * The output array. \b Note: It is the responsibility of the client to - * release the allocated memory after use. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_knots(const tsBSpline *spline, - tsReal **knots, - tsStatus *status); - -/** - * Returns the knot of \p spline at \p index. - * - * @param[in] spline - * The spline whose knot is read at \p index. - * @param[in] index - * Zero-based index of the knot to be read. - * @param[out] knot - * The output value. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_INDEX_ERROR - * If \p index is out of range. - */ -tsError TINYSPLINE_API -ts_bspline_knot_at(const tsBSpline *spline, - size_t index, - tsReal *knot, - tsStatus *status); - -/** - * Sets the knots of \p spline. Creates a deep copy of \p knots. - * - * @pre - * \p knots has length ::ts_bspline_num_knots. - * @param[out] spline - * The spline whose knots are set. - * @param[in] knots - * The value. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_KNOTS_DECR - * If the knot vector is decreasing. - * @return TS_MULTIPLICITY - * If there is a knot with multiplicity > order - */ -tsError TINYSPLINE_API -ts_bspline_set_knots(tsBSpline *spline, - const tsReal *knots, - tsStatus *status); - -/** - * Sets the knots of \p spline supplied as varargs. As all splines have at - * least two knots, the first two knots have a named parameter. Note that, by - * design of varargs in C, the last named parameter must not be float. Thus, - * \p knot1 is of type double instead of ::tsReal. - * - * @pre - * ::ts_bspline_num_knots knots are supplied as varargs. - * @param[out] spline - * The spline whose knots are set. - * @param[out] status - * The status of this function. May be NULL. - * @param[in] knot0 - * The first knot. - * @param[in] knot1 - * the second knot. - * @param[in] ... - * The remaining knots. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - * @return TS_KNOTS_DECR - * If the knot vector is decreasing. - * @return TS_MULTIPLICITY - * If there is a knot with multiplicity > order - */ -tsError TINYSPLINE_API -ts_bspline_set_knots_varargs(tsBSpline *spline, - tsStatus *status, - tsReal knot0, - double knot1, - ...); - -/** - * Sets the knot of \p spline at \p index. - * - * @param[in] spline - * The spline whose knot is set at \p index. - * @param[in] index - * Zero-based index of the knot to be set. - * @param[in] knot - * The value. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_INDEX_ERROR - * If \p index is out of range. - * @return TS_KNOTS_DECR - * If setting the knot at \p index results in a decreasing knot vector. - * @return TS_MULTIPLICITY - * If setting the knot at \p index results in a knot vector containing - * \p knot with multiplicity greater than the order of \p spline. - */ -tsError TINYSPLINE_API -ts_bspline_set_knot_at(tsBSpline *spline, - size_t index, - tsReal knot, - tsStatus *status); -/*! @} */ - - - -/*! @name B-Spline Initialization - * - * The following functions are used to create and release ::tsBSpline instances - * as well as to copy and move the internal data of a ::tsBSpline instance to - * another instance. - * - * \b Note: It is recommended to initialize an instance with - * ::ts_bspline_init. This way, ::ts_bspline_free can be called in ::TS_CATCH - * and ::TS_FINALLY blocks (see Section Error Handling for more details) - * without further checking. For example: - * - * tsBSpline spline = ts_bspline_init(); - * TS_TRY(...) - * ... - * TS_FINALLY - * ts_bspline_free(&spline); - * TS_END_TRY - * - * @{ - */ -/** - * Creates a new spline whose data points to NULL. - * - * @return - * A new spline whose data points to NULL. - */ -tsBSpline TINYSPLINE_API -ts_bspline_init(void); - -/** - * Creates a new spline and stores the result in \p spline. - * - * @param[in] num_control_points - * The number of control points of \p spline. - * @param[in] dimension - * The dimension of the control points of \p spline. - * @param[in] degree - * The degree of \p spline. - * @param[in] type - * How to setup the knot vector of \p spline. - * @param[out] spline - * The output spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_DIM_ZERO - * If \p dimension is \c 0. - * @return TS_DEG_GE_NCTRLP - * If \p degree >= \p num_control_points. - * @return TS_NUM_KNOTS - * If \p type is ::TS_BEZIERS and - * (\p num_control_points % \p degree + 1) != 0. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_new(size_t num_control_points, - size_t dimension, - size_t degree, - tsBSplineType type, - tsBSpline *spline, - tsStatus *status); - -/** - * Creates a new spline with given control points (varargs) and stores the - * result in \p spline. As all splines have at least one control point (with - * minimum dimensionality one), the first component of the first control point - * has a named parameter. Note that, by design of varargs in C, the last named - * parameter must not be float. Thus, \p first is of type double instead of - * ::tsReal. - * - * @param[in] num_control_points - * The number of control points of \p spline. - * @param[in] dimension - * The dimension of the control points of \p spline. - * @param[in] degree - * The degree of \p spline. - * @param[in] type - * How to setup the knot vector of \p spline. - * @param[out] spline - * The output spline. - * @param[out] status - * The status of this function. May be NULL. - * @param[in] first - * The first component of the first control point. - * @param[in] ... - * The remaining components (control points). - * @return TS_SUCCESS - * On success. - * @return TS_DIM_ZERO - * If \p dimension is \c 0. - * @return TS_DEG_GE_NCTRLP - * If \p degree >= \p num_control_points. - * @return TS_NUM_KNOTS - * If \p type is ::TS_BEZIERS and - * (\p num_control_points % \p degree + 1) != 0. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_new_with_control_points(size_t num_control_points, - size_t dimension, - size_t degree, - tsBSplineType type, - tsBSpline *spline, - tsStatus *status, - double first, - ...); - -/** - * Creates a deep copy of \p src and stores the copied data in \p dest. \p src - * and \p dest can be the same instance. - * - * \b Note: Unlike \e memcpy and \e memmove, the first parameter is the source - * and the second parameter is the destination. - * - * @param[in] src - * The spline to be deep copied. - * @param[out] dest - * The output spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_copy(const tsBSpline *src, - tsBSpline *dest, - tsStatus *status); - -/** - * Moves the ownership of the data of \p src to \p dest. After calling this - * function, the data of \p src points to NULL. Does not release the data of \p - * dest. \p src and \p dest can be the same instance (in this case, the data of - * \p src remains). - * - * @param[in, out] src - * The spline whose data is moved to \p dest. - * @param[out] dest - * The spline that receives the data of \p src. - */ -void TINYSPLINE_API -ts_bspline_move(tsBSpline *src, - tsBSpline *dest); - -/** - * Releases the data of \p spline. After calling this function, the data of \p - * spline points to NULL. - * - * @param[out] spline - * The spline to be released. - */ -void TINYSPLINE_API -ts_bspline_free(tsBSpline *spline); -/*! @} */ - - - -/*! @name De Boor Net Data - * - * The internal state of ::tsDeBoorNet is protected using the PIMPL design - * pattern (see https://en.cppreference.com/w/cpp/language/pimpl for more - * details). The data of an instance can be accessed with the functions listed - * in this section. - * - * @{ - */ -/** - * Represents the output of De Boor's algorithm. De Boor's algorithm is used to - * evaluate a spline at a certain knot by iteratively computing a net of - * intermediate points until the resulting point is available: - * - * https://en.wikipedia.org/wiki/De_Boor%27s_algorithm - * https://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/de-Boor.html - * - * All points of a net are stored in \c points (::ts_deboornet_points). The - * resulting point is the last point in \c points and, for the sake of - * convenience, can be accessed with ::ts_deboornet_result. - * - * Two dimensional points are stored as follows: - * - * [x_0, y_0, x_1, y_1, ..., x_n-1, y_n-1] - * - * Tree dimensional points are stored as follows: - * - * [x_0, y_0, z_0, x_1, y_1, z_1, ..., x_n-1, y_n-1, z_n-1] - * - * ... and so on. The output also supports homogeneous coordinates - * (cf. ::tsBSpline). - * - * There is a special case in which the evaluation of a knot \c u returns two - * results instead of one. It occurs when the multiplicity of \c u (\c s(u)) is - * equals to the order of the evaluated spline, indicating that the spline is - * discontinuous at \c u. This is common practice for B-Splines (and NURBS) - * consisting of connected Bezier curves where the endpoint of curve \c c_i is - * equal to the start point of curve \c c_i+1. Yet, the end point of \c c_i and - * the start point of \c c_i+1 may still be completely different, yielding to - * visible gaps (if distance of the points is large enough). In such case (\c - * s(u) == \c order), ::ts_deboornet_points stores only the two resulting - * points (there is no net to calculate) and ::ts_deboornet_result points to - * the \e first point in ::ts_deboornet_points. Since having gaps in splines is - * unusual, both points in ::ts_deboornet_points are generally equal, making it - * easy to handle this special case by simply calling - * ::ts_deboornet_result. However, one can access both points if necessary: - * - * ts_deboornet_result(...)[0] ... // Access the first component of - * // the first result. - * - * ts_deboornet_result(...)[dim(spline)] // Access the first component of - * // the second result. - * - * As if this wasn't complicated enough, there is an exception for this special - * case, yielding to exactly one result (just like the regular case) even if \c - * s(u) == \c order. It occurs when \c u is the lower or upper bound of the - * domain of the evaluated spline. For instance, if \c b is a spline with - * domain [0, 1] and \c b is evaluated at \c u = \c 0 or \c u = \c 1, then - * ::ts_deboornet_result is \e always a single point regardless of the - * multiplicity of \c u. - * - * In summary, there are three different types of evaluation: - * - * 1. The regular case, in which all points of the net are returned. - * - * 2. A special case, in which two results are returned (required for splines - * with gaps). - * - * 3. The exception of 2., in which exactly one result is returned (even if \c - * s(u) == \c order). - * - * All in all this looks quite complex (and actually it is), but for most - * applications you do not have to deal with this. Just use - * ::ts_deboornet_result to access the outcome of De Boor's algorithm. - */ -typedef struct -{ - struct tsDeBoorNetImpl *pImpl; /**< The actual implementation. */ -} tsDeBoorNet; - -/** - * Returns the knot (sometimes also referred to as \c u or \c t) of \p net. - * - * @param[in] net - * The net whose knot is read. - * @return - * The knot of \p net. - */ -tsReal TINYSPLINE_API -ts_deboornet_knot(const tsDeBoorNet *net); - -/** - * Returns the index of the knot of \p net. - * - * @param[in] net - * The net whose index is read. - * @return - * The index [u_k, u_k+1) with \c u being the knot of \p net. - */ -size_t TINYSPLINE_API -ts_deboornet_index(const tsDeBoorNet *net); - -/** - * Returns the multiplicity of the knot of \p net. - * - * @param[in] net - * The net whose multiplicity is read. - * @return - * The multiplicity of the knot of \p net. - */ -size_t TINYSPLINE_API -ts_deboornet_multiplicity(const tsDeBoorNet *net); - -/** - * Returns the number of insertion that were necessary to evaluate the knot of - * \p net. - * - * @param[in] net - * The net whose number of insertions of its knot is read. - * @return - * The number of insertions that were necessary to evaluate the knot of \p - * net. - */ -size_t TINYSPLINE_API -ts_deboornet_num_insertions(const tsDeBoorNet *net); - -/** - * Returns the dimensionality of \p net, that is, the number of components of - * its points (::ts_deboornet_points) and result (::ts_deboornet_result). - * One-dimensional nets are possible, albeit their benefit might be - * questionable. - * - * @param[in] net - * The net whose dimension is read. - * @return - * The dimensionality of \p net (>= 1). - */ -size_t TINYSPLINE_API -ts_deboornet_dimension(const tsDeBoorNet *net); - -/** - * Returns the length of the point array of \p net. - * - * @param[in] net - * The net whose length of the point array is read. - * @return - * The length of the point array of \p net. - */ -size_t TINYSPLINE_API -ts_deboornet_len_points(const tsDeBoorNet *net); - -/** - * Returns the number of points of \p net. - * - * @param[in] net - * The net whose number of points is read. - * @return - * The number of points of \p net. - */ -size_t TINYSPLINE_API -ts_deboornet_num_points(const tsDeBoorNet *net); - -/** - * Returns the size of the point array of \p net. This function may be useful - * when copying points using \e memcpy or \e memmove. - * - * @param[in] net - * The net whose size of the point array is read. - * @return - * The size of the point array of \p net. - */ -size_t TINYSPLINE_API -ts_deboornet_sof_points(const tsDeBoorNet *net); - -/** - * Returns the pointer to the point array of \p net. Note that the return type - * of this function is \c const for a reason. Clients should only read the - * returned array. When suppressing the constness and writing to the array - * against better knowledge, the client is on its own with regard to the - * consistency of the internal state of \p net. To obtain a copy of the points - * of \p net, use ::ts_deboornet_points. - * - * @param[in] net - * The net whose pointer to the point array is returned. - * @return - * Pointer to the point array of \p net. - */ -const tsReal TINYSPLINE_API * -ts_deboornet_points_ptr(const tsDeBoorNet *net); - -/** - * Returns a deep copy of the points of \p net. - * - * @param[in] net - * The net whose points are read. - * @param[out] points - * The output array. \b Note: It is the responsibility of the client to - * release the allocated memory after use. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_deboornet_points(const tsDeBoorNet *net, - tsReal **points, - tsStatus *status); - -/** - * Returns the length of the result array of \p net. - * - * @param[in] net - * The net whose length of the result array is read. - * @return - * The length of the result array of \p net. - */ -size_t TINYSPLINE_API -ts_deboornet_len_result(const tsDeBoorNet *net); - -/** - * Returns the number of points in the result array of \p net - * (1 <= num_result <= 2). - * - * @param[in] net - * The net whose number of points in the result array is read. - * @return - * The number of points in the result array of \p net. - */ -size_t TINYSPLINE_API -ts_deboornet_num_result(const tsDeBoorNet *net); - -/** - * Returns the size of the result array of \p net. This function may be useful - * when copying results using \e memcpy or \e memmove. - * - * @param[in] net - * The net whose size of the result array is read. - * @return TS_SUCCESS - * The size of the result array of \p net. - */ -size_t TINYSPLINE_API -ts_deboornet_sof_result(const tsDeBoorNet *net); - -/** - * Returns the pointer to the result array of \p net. Note that the return type - * of this function is \c const for a reason. Clients should only read the - * returned array. When suppressing the constness and writing to the array - * against better knowledge, the client is on its own with regard to the - * consistency of the internal state of \p net. To obtain a copy of the result - * of \p net, use ::ts_deboornet_result. - * - * @param[in] net - * The net whose pointer to the result array is returned. - * @return - * Pointer to the result array of \p net. - */ -const tsReal TINYSPLINE_API * -ts_deboornet_result_ptr(const tsDeBoorNet *net); - -/** - * Returns a deep copy of the result of \p net. - * - * @param[in] net - * The net whose result is read. - * @param[out] result - * The output array. \b Note: It is the responsibility of the client to - * release the allocated memory after use. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_deboornet_result(const tsDeBoorNet *net, - tsReal **result, - tsStatus *status); -/*! @} */ - - - -/*! @name De Boor Net Initialization - * - * The following functions are used to create and release ::tsDeBoorNet - * instances as well as to copy and move the internal data of a ::tsDeBoorNet - * instance to another instance. - * - * \b Note: It is recommended to initialize an instance with - * ::ts_deboornet_init. This way, ::ts_deboornet_free can be called in - * ::TS_CATCH and ::TS_FINALLY blocks (see Section Error Handling for more - * details) without further checking. For example: - * - * tsDeBoorNet net = ts_deboornet_init(); - * TS_TRY(...) - * ... - * TS_FINALLY - * ts_deboornet_free(&net); - * TS_END_TRY - * - * @{ - */ -/** - * Creates a new net whose data points to NULL. - * - * @return - * A new net whose data points to NULL. - */ -tsDeBoorNet TINYSPLINE_API -ts_deboornet_init(void); - -/** - * Creates a deep copy of \p src and stores the copied data in \p dest. \p src - * and \p dest can be the same instance. - * - * \b Note: Unlike \e memcpy and \e memmove, the first parameter is the source - * and the second parameter is the destination. - * - * @param[in] src - * The net to be deep copied. - * @param[out] dest - * The output net. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_deboornet_copy(const tsDeBoorNet *src, - tsDeBoorNet *dest, - tsStatus *status); - -/** - * Moves the ownership of the data of \p src to \p dest. After calling this - * function, the data of \p src points to NULL. Does not release the data of \p - * dest. \p src and \p dest can be the same instance (in this case, the data of - * \p src remains). - * - * @param[out] src - * The net whose data is moved to \p dest. - * @param[out] dest - * The net that receives the data of \p src. - */ -void TINYSPLINE_API -ts_deboornet_move(tsDeBoorNet *src, - tsDeBoorNet *dest); - -/** - * Releases the data of \p net. After calling this function, the data of \p net - * points to NULL. - * - * @param[out] net - * The net to be released. - */ -void TINYSPLINE_API -ts_deboornet_free(tsDeBoorNet *net); -/*! @} */ - - - -/*! @name Interpolation and Approximation Functions - * - * Given a set (or a sequence) of points, interpolate/approximate a spline that - * follows these points. - * - * Note: Approximations have not yet been implemented. Pull requests are - * welcome. - * - * @{ - */ -/** - * Interpolates a cubic spline with natural end conditions. For more details - * see: - * - * https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm - * http://www.math.ucla.edu/~baker/149.1.02w/handouts/dd_splines.pdf - * http://www.bakoma-tex.com/doc/generic/pst-bspline/pst-bspline-doc.pdf - * - * The interpolated spline is a sequence of bezier curves connecting each point - * in \p points. Each bezier curve is of degree \c 3 with dimensionality \p - * dimension. The total number of control points is: - * - * min(1, \p num_points - 1) * 4 - * - * Note: \p num_points is the number of points in \p points and not the length - * of \p points. For instance, the following point vector has - * \p num_points = 4 and \p dimension = 2: - * - * [x0, y0, x1, y1, x2, y2, x3, y3] - * - * @param[in] points - * The points to be interpolated. - * @param[in] num_points - * The number of points in \p points. If \c 1, a cubic point (i.e., a - * spline with four times the same control point) is created. - * @param[in] dimension - * The dimensionality of the points. - * @param[out] spline - * The interpolated spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_DIM_ZERO - * If \p dimension is 0. - * @return TS_NUM_POINTS - * If \p num_points is 0. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_interpolate_cubic_natural(const tsReal *points, - size_t num_points, - size_t dimension, - tsBSpline *spline, - tsStatus *status); - -/** - * Interpolates a piecewise cubic spline by translating the given catmull-rom - * control points into a sequence of bezier curves. In order to avoid division - * by zero, successive control points with distance less than or equal to \p - * epsilon are filtered out. If the resultant sequence contains only a single - * point, a cubic point (i.e., a spline with four times the same control point) - * is created. Optionally, the first and last control point can be specified - * (see \p first and \p last). - * - * @param[in] points - * The points to be interpolated. - * @param[in] num_points - * The number of points in \p points. If \c 1, a cubic point (i.e., a - * spline with four times the same control point) is created. - * @param[in] dimension - * The dimensionality of the points. - * @param[in] alpha - * Knot parameterization: 0 => uniform, 0.5 => centripetal, 1 => chordal. - * The input value is clamped to the domain [0, 1]. - * @param[in] first - * The first control point of the catmull-rom sequence. If NULL, an - * appropriate point is generated based on the first two points in - * \p points. If the distance between \p first and the first control point - * in \p points is less than or equals to \p epsilon, \p first is treated - * as NULL. This is necessary to avoid division by zero. - * @param[in] last - * The last control point of the catmull-rom sequence. If NULL, an - * appropriate point is generated based on the last two points in - * \p points. If the distance between \p last and the last control point - * in \p points is less than or equals to \p epsilon, \p last is treated - * as NULL. This is necessary to avoid division by zero. - * @param[in] epsilon - * The maximum distance between points with "same" coordinates. That is, - * if the distance between neighboring points is less than or equal to - * \p epsilon, they are considered equal. For the sake of fail-safeness, - * the sign is removed with fabs. It is advisable to pass a value greater - * than zero, however, it is not necessary. - * @param[out] spline - * The interpolated spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_DIM_ZERO - * If \p dimension is 0. - * @return TS_NUM_POINTS - * If \p num_points is 0. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_interpolate_catmull_rom(const tsReal *points, - size_t num_points, - size_t dimension, - tsReal alpha, - const tsReal *first, - const tsReal *last, - tsReal epsilon, - tsBSpline *spline, - tsStatus *status); -/*! @} */ - - - -/*! @name Query Functions - * - * Functions for querying different kinds of data from splines. - * - * @{ - */ -/** - * Evaluates \p spline at \p knot and stores the result (see ::tsDeBoorNet) in - * \p net. - * - * @param[in] spline - * The spline to evaluate. - * @param[in] knot - * The knot to evaluate \p spline at. - * @param[out] net - * Stores the evaluation result. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_U_UNDEFINED - * If \p spline is not defined at knot value \p u. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_eval(const tsBSpline *spline, - tsReal knot, - tsDeBoorNet *net, - tsStatus *status); - -/** - * Evaluates \p spline at each knot in \p knots and stores the evaluated points - * (see ::ts_deboornet_result) in \p points. If \p knots contains one or more - * knots where \p spline is discontinuous at, only the first point of the - * corresponding evaluation result is taken. After calling this function \p - * points contains exactly \code num * ts_bspline_dimension(spline) \endcode - * values. - * - * This function is in particular useful in cases where a multitude of knots - * need to be evaluated, because only a single instance of ::tsDeBoorNet is - * created and reused for all evaluation tasks (therefore the memory footprint - * is reduced to a minimum). - * - * @param[in] spline - * The spline to evaluate. - * @param[in] knots - * The knot values to evaluate. - * @param[in] num - * The number of knots in \p us. - * @param[out] points - * The output parameter. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_U_UNDEFINED - * If \p spline is not defined at one of the knot values in \p us. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_eval_all(const tsBSpline *spline, - const tsReal *knots, - size_t num, - tsReal **points, - tsStatus *status); - -/** - * Generates a sequence of \p num different knots, passes this sequence to - * ::ts_bspline_eval_all, and stores the resultant points in \p points. The - * sequence of knots is generated using ::ts_bspline_uniform_knot_seq. If \p - * num is 0, the default value \c 100 is used as fallback. - * - * For the sake of stability regarding future changes, the actual number of - * generated knots (which only differs from \p num if \p num is 0) is stored in - * \p actual_num. If \p num is 1, the point located at the minimum of the - * domain of \p spline is evaluated. - * - * @param[in] spline - * The spline to be evaluate. - * @param[in] num - * The number of knots to be generate. - * @param[out] points - * The output parameter. - * @param[out] actual_num - * The actual number of generated knots. Differs from \p num only if - * \p num is 0. Must not be NULL. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_sample(const tsBSpline *spline, - size_t num, - tsReal **points, - size_t *actual_num, - tsStatus *status); - -/** - * Tries to find a point P on \p spline such that: - * - * ts_distance(P[index], value, 1) <= fabs(epsilon) - * - * This function is using the bisection method to determine P. Accordingly, it - * is expected that the control points of \p spline are sorted at component - * \p index either in ascending order (if \p ascending != 0) or in descending - * order (if \p ascending == 0). If the control points of \p spline are not - * sorted at component \p index, the behaviour of this function is undefined. - * For the sake of fail-safeness, the distance of P[index] and \p value is - * compared with the absolute value of \p epsilon (using fabs). - * - * The bisection method is an iterative approach which minimizes the error - * (\p epsilon) with each iteration step until an "optimum" was found. However, - * there may be no point P satisfying the distance condition. Thus, the number - * of iterations must be limited (\p max_iter). Depending on the domain of the - * control points of \p spline at component \p index and \p epsilon, - * \p max_iter ranges from 7 to 50. In most cases \p max_iter == 30 should be - * fine though. The parameter \p persnickety allows to define the behaviour of - * this function is case no point was found after \p max_iter iterations. If - * enabled (!= 0), TS_NO_RESULT is returned. If disabled (== 0), the best - * fitting point is returned. - * - * @param[in] spline - * The spline to evaluate - * @param[in] value - * The value (point at component \p index) to find. - * @param[in] epsilon - * The maximum distance (inclusive). - * @param[in] persnickety - * Indicates whether TS_NO_RESULT should be returned if there is no point - * P satisfying the distance condition (!= 0 to enable, == 0 to disable). - * If disabled, the best fitting point is returned. - * @param[in] index - * The point's component. - * @param[in] ascending - * Indicates whether the control points of \p spline are sorted in - * ascending (!= 0) or in descending (== 0) order at component \p index. - * @param[in] max_iter - * The maximum number of iterations (30 is a sane default value). - * @param[out] net - * The output parameter. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_INDEX_ERROR - * If the dimension of the control points of \p spline <= \p index. - * @return TS_NO_RESULT - * If \p persnickety is enabled (!= 0) and there is no point P satisfying - * the distance condition. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_bisect(const tsBSpline *spline, - tsReal value, - tsReal epsilon, - int persnickety, - size_t index, - int ascending, - size_t max_iter, - tsDeBoorNet *net, - tsStatus *status); - -/** - * Returns the domain of \p spline. - * - * @param[in] spline - * The spline to query. - * @param[out] min - * The lower bound of the domain of \p spline. - * @param[out] max - * The upper bound of the domain of \p spline. - */ -void TINYSPLINE_API -ts_bspline_domain(const tsBSpline *spline, - tsReal *min, - tsReal *max); - -/** - * Checks whether the distance of the endpoints of \p spline is less than or - * equal to \p epsilon for the first 'ts_bspline_degree - 1' derivatives - * (starting with the zeroth derivative). - * - * @param[in] spline - * The spline to query. - * @param[in] epsilon - * The maximum distance. - * @param[out] closed - * The output parameter. 1 if true, 0 otherwise. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_is_closed(const tsBSpline *spline, - tsReal epsilon, - int *closed, - tsStatus *status); - -/** - * Computes a sequence of three-dimensional frames (see ::tsFrame) for the - * spline \p spline. The position of the frames corresponds to the knots in \p - * knots. The implementation is based on: - * - * @article{10.1145/1330511.1330513, - * author = {Wang, Wenping and J\"{u}ttler, Bert and Zheng, Dayue - * and Liu, Yang}, - * title = {Computation of Rotation Minimizing Frames}, - * year = {2008}, - * issue_date = {March 2008}, - * publisher = {Association for Computing Machinery}, - * address = {New York, NY, USA}, - * volume = {27}, - * number = {1}, - * issn = {0730-0301}, - * url = {https://doi.org/10.1145/1330511.1330513}, - * doi = {10.1145/1330511.1330513}, - * abstract = {Due to its minimal twist, the rotation minimizing - * frame (RMF) is widely used in computer graphics, - * including sweep or blending surface modeling, motion - * design and control in computer animation and - * robotics, streamline visualization, and tool path - * planning in CAD/CAM. We present a novel simple and - * efficient method for accurate and stable computation - * of RMF of a curve in 3D. This method, called the - * double reflection method, uses two reflections to - * compute each frame from its preceding one to yield a - * sequence of frames to approximate an exact RMF. The - * double reflection method has the fourth order global - * approximation error, thus it is much more accurate - * than the two currently prevailing methods with the - * second order approximation error—the projection - * method by Klok and the rotation method by - * Bloomenthal, while all these methods have nearly the - * same per-frame computational cost. Furthermore, the - * double reflection method is much simpler and faster - * than using the standard fourth order Runge-Kutta - * method to integrate the defining ODE of the RMF, - * though they have the same accuracy. We also - * investigate further properties and extensions of the - * double reflection method, and discuss the - * variational principles in design moving frames with - * boundary conditions, based on RMF.}, - * journal = {ACM Trans. Graph.}, - * month = mar, - * articleno = {2}, - * numpages = {18}, - * keywords = {motion design, sweep surface, motion, differential - * geometry, Curve, rotation minimizing frame} - * } - * - * @pre \p knots and \p frames have \p num entries. - * @param[in] spline - * The spline to query. - * @param[in] knots - * The knots to query \p spline at. - * @param[in] num - * Number of elements in \p knots and \p frames. Can be \c 0. - * @param[in] has_first_normal - * Indicates whether the normal of the first element of \p frames should - * be taken as starting value for the algorithm. If \c 0, the starting - * normal is determined based on the tangent of \p spline at \c knots[0]. - * Note that, if the argument value is not \c 0, it is up to the caller of - * this function to ensure that the supplied normal is valid. The function - * only normalizes the supplied value. - * @param[in, out] frames - * Stores the computed frames. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If memory allocation failed. - */ -tsError TINYSPLINE_API -ts_bspline_compute_rmf(const tsBSpline *spline, - const tsReal *knots, - size_t num, - int has_first_normal, - tsFrame *frames, - tsStatus *status); - -/** - * Generates a sequence of \p num knots with uniform distribution. \e Uniform - * means that consecutive knots have the same distance. - * - * @param[in] spline - * The spline to query. - * @param[in] num - * Number of knots in \p knots. - * @param[out] knots - * Stores the generated knot sequence. - */ -void TINYSPLINE_API -ts_bspline_uniform_knot_seq(const tsBSpline *spline, - size_t num, - tsReal *knots); -/*! @} */ - - - -/*! @name Transformation Functions - * - * A transformation modifies the internal state of a spline, e.g., its number - * of control points, the structure of its knot vector, its degree, and so - * on. It should be noted that some transformation modify a spline's state - * without changing its shape (e.g., ::ts_bspline_elevate). All transformations - * specify at least three parameters: i) an input spline (the spline to be - * transformed), ii) an output spline (the spline which receives the result of - * the transformation), and iii) a ::tsStatus (output parameter for error - * handling). Along with these parameters, additional parameters may be - * necessary to i) calculate a certain transformation (such as - * ::ts_bspline_tension) or ii) to store additional results (such as - * ::ts_bspline_insert_knot). Unless stated otherwise, the order of the - * parameters of a transformation \c t is: - * - * t(input, [additional_input], output, [additional_output], status) - * - * \b Note: None of the transformations releases the memory of the output - * spline before assigning the transformation result to it. Thus, when using - * the same output spline multiple times, make sure to release its memory - * before each call (after the first one). If not, severe memory leaks are to - * be expected: - * - * tsBSpline in = ... // an arbitrary spline - * tsBSpline out = ts_bspline_init(); // stores the result - * - * ts_bspline_to_beziers(&in, &out); // first transformation - * ... // some code - * ts_bspline_free(&out); // avoid memory leak. - * ts_bspline_tension(&in, 0.85, &out); // next transformation - * - * It is possible to pass a spline as input and output argument at the same - * time. In this case, the called transformation uses a temporary buffer to - * store the working data and result. If the transformation succeeds, the - * memory of the supplied spline is released and the transformation result is - * assigned to it. So even if a transformation fails, the internal state of the - * supplied splines stays intact (i.e., it remains unchanged). - * - * \b Note: It is not necessary to release the memory of a spline which is - * passed as input and output argument at the same time before calling the next - * transformation (in fact that would fail due to a null pointer): - * - * tsBSpline spline = ... // an arbitrary spline - * ts_bspline_to_beziers(&spline, &spline); // first transformation - * ts_bspline_tension(&spline, 0.85, &spline); // next transformation - * - * @{ - */ -/** - * Returns the \p n'th derivative of \p spline as ::tsBSpline instance. The - * derivative of a spline \c s of degree \c d (\c d > 0) with \c m control - * points and \c n knots is another spline \c s' of degree \c d-1 with \c m-1 - * control points and \c n-2 knots, defined over \c s as: - * - * \f{eqnarray*}{ - * s'(u) &=& \sum_{i=0}^{n-1} N_{i+1,p-1}(u) * - * (P_{i+1} - P_{i}) * - * p / (u_{i+p+1}-u_{i+1}) \\ - * &=& \sum_{i=1}^{n} N_{i,p-1}(u) * - * (P_{i} - P_{i-1}) * - * p / (u_{i+p}-u_{i}) - * \f} - * - * If \c s has a clamped knot vector, it can be shown that: - * - * \f{eqnarray*}{ - * s'(u) &=& \sum_{i=0}^{n-1} N_{i,p-1}(u) * - * (P_{i+1} - P_{i}) * - * p / (u_{i+p+1}-u_{i+1}) - * \f} - * - * where the multiplicity of the first and the last knot value \c u is \c p - * rather than \c p+1. The derivative of a point (degree 0) is another point - * with coordinate 0. For more details, see: - * - * http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-derv.html - * - * If \p spline != \p deriv, the internal state of \p spline is not modified, - * that is, \p deriv is a new, independent ::tsBSpline instance. - * - * @param[in] spline - * The spline to be derived. - * @param[in] n - * Number of derivations. - * @param[in] epsilon - * The maximum distance of discontinuous points. If negative, - * discontinuity is ignored and the derivative is computed based on the - * first result of the corresponding ::tsDeboorNet. - * @param[out] deriv - * The derivative of \p spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_UNDERIVABLE - * If \p spline is discontinuous at an internal knot and the distance - * between the corresponding points is greater than \p epsilon. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_derive(const tsBSpline *spline, - size_t n, - tsReal epsilon, - tsBSpline *deriv, - tsStatus *status); - -/** - * Inserts \p knot \p num times into the knot vector of \p spline. The - * operation fails if \p result would have an invalid knot vector (i.e., - * multiplicity(knot) > order(result)). If \p spline != \p result, the internal - * state of \p spline is not modified, that is, \p result is a new, independent - * ::tsBSpline instance. - * - * @param[in] spline - * The spline into which \p knot is inserted \p num times. - * @param[in] knot - * The knot to be inserted. - * @param[in] num - * Number of insertions. - * @param[out] result - * The output spline. - * @param[out] k - * Stores the last index of \p knot in \p result. - * @param status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_U_UNDEFINED - * If \p knot is not within the domain of \p spline. - * @return TS_MULTIPLICITY - * If the multiplicity of \p knot in \p spline plus \p num is greater than - * the order of \p spline. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_insert_knot(const tsBSpline *spline, - tsReal knot, size_t num, - tsBSpline *result, - size_t *k, - tsStatus *status); - -/** - * Splits \p spline at \p knot. That is, \p knot is inserted into \p spline \c - * n times such that the multiplicity of \p knot is equal the spline's order. - * If \p spline != \p split, the internal state of \p spline is not modified, - * that is, \p split is a new, independent ::tsBSpline instance. - * - * @param[in] spline - * The spline to be split. - * @param[in] knot - * The split point (knot). - * @param[out] split - * The split spline. - * @param[out] k - * Stores the last index of \p knot in \p split. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_U_UNDEFINED - * If \p spline is not defined at \p knot. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_split(const tsBSpline *spline, - tsReal knot, - tsBSpline *split, - size_t *k, - tsStatus *status); - -/** - * Straightens the control points of \p spline according to \p beta (0 yields a - * line connecting the first and the last control point; 1 keeps the original - * shape). The value of \p beta is clamped to [0, 1]. If \p spline != \p out, - * the internal state of \p spline is not modified, that is, \p out is a new, - * independent ::tsBSpline instance. - * - * This function is based on: - * - * @ARTICLE{10.1109/TVCG.2006.147, - * author = {Holten, Danny}, - * journal = {IEEE Transactions on Visualization and Computer - * Graphics}, - * title = {Hierarchical Edge Bundles: Visualization of - * Adjacency Relations in Hierarchical Data}, - * year = {2006}, - * volume = {12}, - * number = {5}, - * pages = {741-748}, - * abstract = {A compound graph is a frequently encountered type of - * data set. Relations are given between items, and a - * hierarchy is defined on the items as well. We - * present a new method for visualizing such compound - * graphs. Our approach is based on visually bundling - * the adjacency edges, i.e., non-hierarchical edges, - * together. We realize this as follows. We assume that - * the hierarchy is shown via a standard tree - * visualization method. Next, we bend each adjacency - * edge, modeled as a B-spline curve, toward the - * polyline defined by the path via the inclusion edges - * from one node to another. This hierarchical bundling - * reduces visual clutter and also visualizes implicit - * adjacency edges between parent nodes that are the - * result of explicit adjacency edges between their - * respective child nodes. Furthermore, hierarchical - * edge bundling is a generic method which can be used - * in conjunction with existing tree visualization - * techniques. We illustrate our technique by providing - * example visualizations and discuss the results based - * on an informal evaluation provided by potential - * users of such visualizations.}, - * keywords = {}, - * doi = {10.1109/TVCG.2006.147}, - * ISSN = {1941-0506}, - * month = {Sep.}, - * } - * - * Holten calls it "straightening" (page 744, equation 1). - * - * @param[in] spline - * The spline to be straightened. - * @param[in] beta - * The straightening factor. The value is clamped to the domain [0, 1]. - * @param[out] out - * The straightened spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_tension(const tsBSpline *spline, - tsReal beta, - tsBSpline *out, - tsStatus *status); - -/** - * Decomposes \p spline into a sequence of Bezier curves by splitting it at - * each internal knot. If \p spline != \p beziers, the internal state of \p - * spline is not modified, that is, \p beziers is a new, independent - * ::tsBSpline instance. - * - * @param[in] spline - * The spline to be decomposed. - * @param[out] beziers - * The bezier decomposition of \p spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_to_beziers(const tsBSpline *spline, - tsBSpline *beziers, - tsStatus *status); - -/** - * Elevates the degree of \p spline by \p amount and stores the result in - * \p elevated. If \p spline != \p elevated, the internal state of \p spline is - * not modified, that is, \p elevated is a new, independent ::tsBSpline - * instance. - * - * @param[in] spline - * The spline to elevate. - * @param[in] amount - * How often to elevate the degree of \p spline. - * @param[in] epsilon - * In order to elevate the degree of a spline, it must be decomposed into - * a sequence of bezier curves (see ::ts_bspline_to_beziers). After degree - * elevation, the split points of the bezier curves are merged again. This - * parameter is used to distinguish between the split points of the - * decomposition process and the wanted discontinuity points. A viable - * default value is ::TS_POINT_EPSILON. - * @param[out] elevated - * The elevated spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If memory allocation failed. - */ -tsError TINYSPLINE_API -ts_bspline_elevate_degree(const tsBSpline *spline, - size_t amount, - tsReal epsilon, - tsBSpline *elevated, - tsStatus *status); - -/** - * Modifies the splines \p s1 and \p s2 such that they have same degree and - * number of control points/knots (without modifying the shape of \p s1 and - * \p s2). The resulting splines are stored in \p s1_out and \p s2_out. If - * \p s1 != \p s1_out, the internal state of \p s1 is not modified, that is, - * \p s1_out is a new, independent ::tsBSpline instance. The same is true for - * \p s2 and \p s2_out. - * - * @param[in] s1 - * The spline which is to be aligned with \p s2. - * @param[in] s2 - * The spline which is to be aligned with \p s1. - * @param[in] epsilon - * Spline alignment relies on degree elevation. This parameter is used in - * ::ts_bspline_elevate_degree to check whether two control points, \c p1 - * and \c p2, are "equal", that is, the distance between \c p1 and \c p2 - * is less than or equal to \p epsilon. A viable default value is - * ::TS_POINT_EPSILON. - * @param[out] s1_out - * The aligned version of \p s1. - * @param[out] s2_out - * The aligned version of \p s2. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If memory allocation failed. - */ -tsError TINYSPLINE_API -ts_bspline_align(const tsBSpline *s1, - const tsBSpline *s2, - tsReal epsilon, - tsBSpline *s1_out, - tsBSpline *s2_out, - tsStatus *status); - -/** - * Interpolates between \p origin and \p target with respect to the time - * parameter \p t (domain: [0, 1]; clamped if necessary). The resulting spline - * is stored in \p out. Because it is to be expected that this function is - * called several times in a row (e.g., to have a smooth transition from one - * spline to another), memory for \p out is allocated only if it points to NULL - * or if it has to be enlarged to store the result of the interpolation (which - * can only happen if \p origin or \p target---or both---have been changed - * since the last call). This way, this function can be used as follows: - * - * tsReal t; - * tsBSpline origin = ... - * tsBSpline target = ... - * tsBSpline morph = ts_bspline_init(); - * for (t = (tsReal) 0.0; t <= (tsReal) 1.0; t += (tsReal) 0.001) - * ts_bspline_morph(&origin, &target, t, ..., &morph, ...); - * ts_bspline_free(&morph); - * - * It should be noted that this function, if necessary, aligns \p origin and \p - * target using ::ts_bspline_align. In order to avoid the overhead of spline - * alignment, \p origin and \p target should be aligned in advance. - * - * @param[in] origin - * Origin spline. - * @param[in] target - * Target spline. - * @param[in] t - * The time parameter. If 0, \p out becomes \p origin. If 1, \p out becomes - * \p target. Note that the value passed is clamped to the domain [0, 1]. - * @param[in] epsilon - * If \p origin and \p target must be aligned, this parameter is passed - * ::ts_bspline_elevate_degree to check whether two control points, \c p1 - * and \c p2, are "equal", that is, the distance between \c p1 and \c p2 - * is less than or equal to \p epsilon. A viable default value is - * ::TS_POINT_EPSILON. - * @param[out] out - * The resulting spline. - * @return[out] TS_SUCCESS - * On success. - * @return TS_MALLOC - * If memory allocation failed. - */ -tsError TINYSPLINE_API -ts_bspline_morph(const tsBSpline *origin, - const tsBSpline *target, - tsReal t, - tsReal epsilon, - tsBSpline *out, - tsStatus *status); -/*! @} */ - - - -/*! @name Serialization and Persistence - * - * The following functions can be used to serialize and persist (i.e., store - * the serialized data in a file) splines. There are also functions to load - * serialized splines. - * - * @{ - */ -/** - * Serializes \p spline to a null-terminated JSON string and stores the result - * in \p json. - * - * @param[in] spline - * The spline to be serialized. - * @param[out] json - * The serialized JSON string. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_to_json(const tsBSpline *spline, - char **json, - tsStatus *status); - -/** - * Parses \p json and stores the result in \p spline. - * - * @param[in] json - * The JSON string to be parsed. - * @param[out] spline - * The output spline. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_PARSE_ERROR - * If an error occurred while parsing \p json. - * @return TS_DIM_ZERO - * If the dimension is \c 0. - * @return TS_LCTRLP_DIM_MISMATCH - * If the length of the control point array modulo dimension is not \c 0. - * @return TS_DEG_GE_NCTRLP - * If the degree is greater or equals to the number of control points. - * @return TS_NUM_KNOTS - * If the number of knots does not match to the number of control points - * plus the degree of the spline. - * @return TS_KNOTS_DECR - * If the knot vector is decreasing. - * @return TS_MULTIPLICITY - * If there is a knot with multiplicity greater than order. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_parse_json(const char *json, - tsBSpline *spline, - tsStatus *status); - -/** - * Saves \p spline as JSON ASCII file. - * - * @param[in] spline - * The spline to be saved. - * @param[in] path - * Path of the JSON file. - * @param[out] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_IO_ERROR - * If an error occurred while saving \p spline. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_save(const tsBSpline *spline, - const char *path, - tsStatus *status); - -/** - * Loads \p spline from a JSON ASCII file. - * - * @param[in] path - * Path of the JSON file to be loaded. - * @param[out] spline - * The output spline. - * @param[ou] status - * The status of this function. May be NULL. - * @return TS_SUCCESS - * On success. - * @return TS_IO_ERROR - * If \p path does not exist. - * @return TS_PARSE_ERROR - * If an error occurred while parsing the contents of \p path. - * @return TS_DIM_ZERO - * If the dimension is \c 0. - * @return TS_LCTRLP_DIM_MISMATCH - * If the length of the control point array modulo dimension is not \c 0. - * @return TS_DEG_GE_NCTRLP - * If the degree is greater or equals to the number of control points. - * @return TS_NUM_KNOTS - * If the number of knots does not match to the number of control points - * plus the degree of the spline. - * @return TS_KNOTS_DECR - * If the knot vector is decreasing. - * @return TS_MULTIPLICITY - * If there is a knot with multiplicity greater than order. - * @return TS_MALLOC - * If allocating memory failed. - */ -tsError TINYSPLINE_API -ts_bspline_load(const char *path, - tsBSpline *spline, - tsStatus *status); - - - -/*! @name Vector Math - * - * Vector math is a not insignificant part of TinySpline, and so it's not - * surprising that some utility functions around vectors are needed. Because - * these functions might be useful for others, they are part of TinySpline's - * public API. However, note that the code is \b not highly optimized (with, - * for example, instruction set extensions like SSE). If high performance - * vector math is needed, other libraries should be used instead. - * - * @{ - */ -/** - * Initializes vector \p out with \p x and \p y. - * - * @pre - * \p out has dimensionality \c 2. - * @param[out] out - * Target vector. - * @param[in] x - * The x value. - * @param[in] y - * The y value. - */ -void TINYSPLINE_API -ts_vec2_init(tsReal *out, - tsReal x, - tsReal y); - -/** - * Initializes vector \p out with \p x, \p y, and \p z. - * - * @pre - * \p out has dimensionality \c 3. - * @param[out] out - * Target vector. - * @param[in] x - * The x value. - * @param[in] y - * The y value. - * @param[in] z - * The z value. - */ -void TINYSPLINE_API -ts_vec3_init(tsReal *out, - tsReal x, - tsReal y, - tsReal z); - -/** - * Initializes vector \p out with \p x, \p y, \p z, and \p w. - * - * @pre - * \p out has dimensionality \c 4. - * @param[out] out - * Target vector. - * @param[in] x - * The x value. - * @param[in] y - * The y value. - * @param[in] z - * The z value. - * @param[in] w - * The w value. - */ -void TINYSPLINE_API -ts_vec4_init(tsReal *out, - tsReal x, - tsReal y, - tsReal z, - tsReal w); - -/** - * Copies the values of vector \p x (a vector with dimensionality \p dim) to - * vector \p out (a vector with dimensionality \c 2). If \p dim is less than \c - * 2, the remaining values of \p out are set to \c 0. Excess values in \p x - * (i.e., \p dim is greater than \c 2) are ignored. - * - * @pre - * \p out has dimensionality \c 2. - * @param[out] out - * Target vector. - * @param[in] x - * Vector to read the values from. - * @param[in] dim - * Dimensionality of \p x. - */ -void TINYSPLINE_API -ts_vec2_set(tsReal *out, - const tsReal *x, - size_t dim); - -/** - * Copies the values of vector \p x (a vector with dimensionality \p dim) to - * vector \p out (a vector with dimensionality \c 3). If \p dim is less than \c - * 3, the remaining values of \p out are set to \c 0. Excess values in \p x - * (i.e., \p dim is greater than \c 3) are ignored. - * - * @pre - * \p out has dimensionality \c 3. - * @param[out] out - * Target vector. - * @param[in] x - * Vector to read the values from. - * @param[in] dim - * Dimensionality of \p x. - */ -void TINYSPLINE_API -ts_vec3_set(tsReal *out, - const tsReal *x, - size_t dim); - -/** - * Copies the values of vector \p x (a vector with dimensionality \p dim) to - * vector \p out (a vector with dimensionality \c 4). If \p dim is less than \c - * 4, the remaining values of \p out are set to \c 0. Excess values in \p x - * (i.e., \p dim is greater than \c 4) are ignored. - * - * @pre - * \p out has dimensionality \c 4. - * @param[out] out - * Target vector. - * @param[in] x - * Vector to read the values from. - * @param[in] dim - * Dimensionality of \p x. - */ -void TINYSPLINE_API -ts_vec4_set(tsReal *out, - const tsReal *x, - size_t dim); - -/** - * Adds vector \p y to vector \p x and stores the result in vector \p out. - * - * @param[in] x - * First vector. - * @param[in] y - * Second vector. - * @param[in] dim - * Dimensionality of \p x, \p y, and \p out. - * @param[out] out - * Result vector. Can be same as \p x or \p y, i.e., the result can be - * stored in-place. - */ -void TINYSPLINE_API -ts_vec_add(const tsReal *x, - const tsReal *y, - size_t dim, - tsReal *out); - -/** - * Subtracts vector \p y from vector \p x and stores the result in vector \p - * out. - * - * @param[in] x - * First vector. - * @param[in] y - * Second vector. - * @param[in] dim - * Dimensionality of \p x, \p y, and \p out. - * @param[out] out - * Result vector. Can be same as \p x or \p y, i.e., the result can be - * stored in-place. - */ -void TINYSPLINE_API -ts_vec_sub(const tsReal *x, - const tsReal *y, - size_t dim, - tsReal *out); - -/** - * Computes the dot product (also known as scalar product) of the vectors \p x - * and \p y. - * - * @post - * \c 0 if \p dim is \c 0. - * @param[in] x - * First vector. - * @param[in] y - * Second vector. - * @param[in] dim - * Dimensionality of \p x and \p y. - * @return - * The dot product of \p x and \y. - */ -tsReal TINYSPLINE_API -ts_vec_dot(const tsReal *x, - const tsReal *y, - size_t dim); - -/** - * Computes the angle in degrees between the vectors \p x and \p y. The angle - * returned is unsigned, that is, the smaller of the two possible angles is - * computed. The nullable parameter \p buf servers as a buffer in case \p x or - * \p y (or both) are not normalized. If \p buf is \c NULL, it is expected that - * \p x and \p y are already normalized. If \p buf is not \c NULL, a storage - * twice the size of \p dim is expected in which the normalized vectors of \p x - * and \p y are stored. - * - * @pre - * \p buf is either \c NULL or has length 2 * dim. - * @param[in] x - * First vector. - * @param[in] y - * Second vector. - * @param[out] buf - * A buffer in which the normalized vectors of \p x and \y are stored. If - * \c NULL, it is expected that \p x and \p y are already normalized. - * @param[in] dim - * Dimensionality of \p x and \p y. - * @return - * The angle between \p x and \y with 0.0 <= angle <= 180.0. - */ -tsReal TINYSPLINE_API -ts_vec_angle(const tsReal *x, - const tsReal *y, - tsReal *buf, - size_t dim); - -/** - * Computes the cross product (also known as vector product or directed area - * product) of the vectors \p x and \p y. - * - * @pre \p x and \p y have dimensionality \c 3. - * @param[in] x - * First vector. - * @param[in] y - * Second vector. - * @param[out] out - * Result vector. Can be same as \p x or \p y, i.e., the result can be - * stored in-place. - */ -void TINYSPLINE_API -ts_vec3_cross(const tsReal *x, - const tsReal *y, - tsReal *out); - -/** - * Normalizes vector \p x. - * - * @post - * \c 0 if the length of \p x (see ::ts_vec_mag) is less than - * ::TS_LENGTH_ZERO. - * @param[in] x - * A vector. - * @param[in] dim - * Dimensionality of \p x. - * @param[out] out - * Result vector. Can be same as \p x, i.e., the result can be stored - * in-place. - */ -void TINYSPLINE_API -ts_vec_norm(const tsReal *x, - size_t dim, - tsReal *out); - -/** - * Determines the length of vector \p x. - * - * @post - * \c 0 if \p dim is \c 0. - * @param[in] x - * A vector. - * @param[in] dim - * Dimensionality of \p x. - */ -tsReal TINYSPLINE_API -ts_vec_mag(const tsReal *x, - size_t dim); - -/** - * Multiplies vector \p x with scalar \p val and stores the result in vector \p - * out. - * - * @param[in] x - * A vector. - * @param[in] dim - * Dimensionality of \p x. - * @param[in] val - * Scalar value. - * @param[out] out - * Result vector. Can be same as \p x, i.e., the result can be stored - * in-place. - */ -void TINYSPLINE_API -ts_vec_mul(const tsReal *x, - size_t dim, - tsReal val, - tsReal *out); -/*! @} */ - - - -/*! @name Utility Functions - * - * @{ - */ -/** - * Returns whether the knots \p x and \p y are equal with respect to the epsilon - * environment ::TS_KNOT_EPSILON (i.e., their distance is less than - * ::TS_KNOT_EPSILON). - * - * @param[in] x - * First knot. - * @param[in] y - * Second knot. - * @return 1 - * If \p x and \p y are equal. - * @return 0 - * If \p x and \p y are not equal. - */ -int TINYSPLINE_API -ts_knots_equal(tsReal x, - tsReal y); - -/** - * Fills the given array \p arr with \p val. - * - * @param[in] arr - * The array to be filled. - * @param[in] num - * Fill length. - * @param[in] val - * The value to fill into \p arr. - */ -void TINYSPLINE_API -ts_arr_fill(tsReal *arr, - size_t num, - tsReal val); - -/** - * Returns the euclidean distance of the points \p x and \p y. - * - * @param[in] x - * First point. - * @param[in] y - * Second point. - * @param[in] dim - * Dimensionality of \p x and \p y. - * @return - * The euclidean distance of the points \p x and \p y. - */ -tsReal TINYSPLINE_API -ts_distance(const tsReal *x, - const tsReal *y, - size_t dim); -/*! @} */ - - - -#ifdef __cplusplus -} -#endif - -#endif /* TINYSPLINE_H */ - diff --git a/source/engine/transform.c b/source/engine/transform.c index f94e04e..1a15ed4 100644 --- a/source/engine/transform.c +++ b/source/engine/transform.c @@ -36,9 +36,28 @@ HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir) return mat3_t_pos(m, dir); } -HMM_Mat3 transform2d2mat(transform2d t) { - HMM_Mat2 m = HMM_MulM2(HMM_RotateM2(t.angle), HMM_ScaleM2(t.scale)); - return HMM_M2BasisPos(m, t.pos); +HMM_Mat3 transform2d2mat(transform2d trn) { + return HMM_MulM3(HMM_Translate2D(trn.pos), HMM_MulM3(HMM_RotateM3(trn.angle), HMM_ScaleM3(trn.scale))); +} + +transform2d mat2transform2d(HMM_Mat3 m) +{ + transform2d t; + t.pos = m.Columns[2].xy; + t.scale = (HMM_Vec2){HMM_LenV2(m.Columns[0].xy), HMM_LenV2(m.Columns[1].xy)}; + t.angle = acos(m.Columns[0].x/t.scale.x); + return t; } HMM_Mat4 transform3d2mat(transform3d t) { return HMM_MulM4(HMM_Translate(t.pos), HMM_MulM4(HMM_QToM4(t.rotation), HMM_Scale(t.scale))); } + +transform3d mat2transform3d(HMM_Mat4 m) +{ + transform3d t; + t.pos = m.Columns[3].xyz; + for (int i = 0; i < 2; i++) + t.scale.Elements[i] = HMM_LenV3(m.Columns[i].xyz); +// for (int i = 0; i < 2; i++) +// m.Columns[i].xyz = HMM_MulV3(m.Columns[i].xyz, t.scale.Elements[i]); + t.rotation = HMM_M4ToQ_RH(m); +} diff --git a/source/engine/transform.h b/source/engine/transform.h index ca63d36..09d0691 100644 --- a/source/engine/transform.h +++ b/source/engine/transform.h @@ -17,6 +17,9 @@ typedef struct { extern const transform2d t2d_unit; +#define VEC2_FMT "[%g,%g]" +#define VEC2_MEMS(s) (s).x, (s).y + HMM_Vec3 trans_forward(const transform3d *const trans); HMM_Vec3 trans_back(const transform3d *trans); HMM_Vec3 trans_up(const transform3d *trans); @@ -38,6 +41,8 @@ HMM_Vec3 mat3_t_pos(HMM_Mat4 m, HMM_Vec3 pos); HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir); HMM_Mat3 transform2d2mat(transform2d t); +transform2d mat2transform2d(HMM_Mat3 m); HMM_Mat4 transform3d2mat(transform3d t); +transform3d mat2transform3d(HMM_Mat4 m); #endif diff --git a/source/engine/yugine.c b/source/engine/yugine.c index 01788f9..828d881 100644 --- a/source/engine/yugine.c +++ b/source/engine/yugine.c @@ -1,11 +1,13 @@ #include "yugine.h" #include "font.h" +#include "transform.h" #include "gameobject.h" #include "input.h" #include "render.h" #include "window.h" #include "sound.h" #include "resources.h" +#include "spline.h" #include #include "datastream.h" @@ -388,7 +390,7 @@ dam->update_activity(dam, &da, NULL, NULL); resources_init(); phys2d_init(); script_startup(); - + int argsize = 0; for (int i = 0; i < argc; i++) { argsize += strlen(argv[i]);