Large refactor - IMGUI to Nuklear

This commit is contained in:
2022-06-21 17:48:19 +00:00
parent b82ea3d670
commit 162aebe3fa
142 changed files with 67753 additions and 181473 deletions

View File

@@ -1,15 +1,15 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
*
* 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
@@ -45,12 +45,12 @@ static inline void
cpCollisionInfoPushContact(struct cpCollisionInfo *info, cpVect p1, cpVect p2, cpHashValue hash)
{
cpAssertSoft(info->count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error: Tried to push too many contacts.");
struct cpContact *con = &info->arr[info->count];
con->r1 = p1;
con->r2 = p2;
con->hash = hash;
info->count++;
}
@@ -64,7 +64,7 @@ PolySupportPointIndex(const int count, const struct cpSplittingPlane *planes, co
{
cpFloat max = -INFINITY;
int index = 0;
for(int i=0; i<count; i++){
cpVect v = planes[i].v0;
cpFloat d = cpvdot(v, n);
@@ -73,7 +73,7 @@ PolySupportPointIndex(const int count, const struct cpSplittingPlane *planes, co
index = i;
}
}
return index;
}
@@ -165,11 +165,11 @@ SupportEdgeForPoly(const cpPolyShape *poly, const cpVect n)
{
int count = poly->count;
int i1 = PolySupportPointIndex(poly->count, poly->planes, n);
// TODO: get rid of mod eventually, very expensive on ARM
int i0 = (i1 - 1 + count)%count;
int i2 = (i1 + 1)%count;
const struct cpSplittingPlane *planes = poly->planes;
cpHashValue hashid = poly->shape.hashid;
if(cpvdot(n, planes[i1].n) > cpvdot(n, planes[i2].n)){
@@ -230,19 +230,19 @@ ClosestPointsNew(const struct MinkowskiPoint v0, const struct MinkowskiPoint v1)
// Find the closest p(t) on the minkowski difference to (0, 0)
cpFloat t = ClosestT(v0.ab, v1.ab);
cpVect p = LerpT(v0.ab, v1.ab, t);
// Interpolate the original support points using the same 't' value as above.
// This gives you the closest surface points in absolute coordinates. NEAT!
cpVect pa = LerpT(v0.a, v1.a, t);
cpVect pb = LerpT(v0.b, v1.b, t);
cpCollisionID id = (v0.id & 0xFFFF)<<16 | (v1.id & 0xFFFF);
// First try calculating the MSA from the minkowski difference edge.
// This gives us a nice, accurate MSA when the surfaces are close together.
cpVect delta = cpvsub(v1.ab, v0.ab);
cpVect n = cpvnormalize(cpvrperp(delta));
cpFloat d = cpvdot(n, p);
if(d <= 0.0f || (-1.0f < t && t < 1.0f)){
// If the shapes are overlapping, or we have a regular vertex/edge collision, we are done.
struct ClosestPoints points = {pa, pb, n, d, id};
@@ -251,7 +251,7 @@ ClosestPointsNew(const struct MinkowskiPoint v0, const struct MinkowskiPoint v1)
// Vertex/vertex collisions need special treatment since the MSA won't be shared with an axis of the minkowski difference.
cpFloat d2 = cpvlength(p);
cpVect n2 = cpvmult(p, 1.0f/(d2 + CPFLOAT_MIN));
struct ClosestPoints points = {pa, pb, n2, d2, id};
return points;
}
@@ -272,7 +272,7 @@ EPARecurse(const struct SupportContext *ctx, const int count, const struct Minko
{
int mini = 0;
cpFloat minDist = INFINITY;
// TODO: precalculate this when building the hull and save a step.
// Find the closest segment hull[i] and hull[i + 1] to (0, 0)
for(int j=0, i=count-1; j<count; i=j, j++){
@@ -282,47 +282,47 @@ EPARecurse(const struct SupportContext *ctx, const int count, const struct Minko
mini = i;
}
}
struct MinkowskiPoint v0 = hull[mini];
struct MinkowskiPoint v1 = hull[(mini + 1)%count];
cpAssertSoft(!cpveql(v0.ab, v1.ab), "Internal Error: EPA vertexes are the same (%d and %d)", mini, (mini + 1)%count);
// Check if there is a point on the minkowski difference beyond this edge.
struct MinkowskiPoint p = Support(ctx, cpvperp(cpvsub(v1.ab, v0.ab)));
#if DRAW_EPA
cpVect verts[count];
for(int i=0; i<count; i++) verts[i] = hull[i].ab;
ChipmunkDebugDrawPolygon(count, verts, 0.0, RGBAColor(1, 1, 0, 1), RGBAColor(1, 1, 0, 0.25));
ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 0, 0, 1));
ChipmunkDebugDrawDot(5, p.ab, LAColor(1, 1));
#endif
// The usual exit condition is a duplicated vertex.
// Much faster to check the ids than to check the signed area.
cpBool duplicate = (p.id == v0.id || p.id == v1.id);
if(!duplicate && cpCheckPointGreater(v0.ab, v1.ab, p.ab) && iteration < MAX_EPA_ITERATIONS){
// Rebuild the convex hull by inserting p.
struct MinkowskiPoint *hull2 = (struct MinkowskiPoint *)alloca((count + 1)*sizeof(struct MinkowskiPoint));
int count2 = 1;
hull2[0] = p;
for(int i=0; i<count; i++){
int index = (mini + 1 + i)%count;
cpVect h0 = hull2[count2 - 1].ab;
cpVect h1 = hull[index].ab;
cpVect h2 = (i + 1 < count ? hull[(index + 1)%count] : p).ab;
if(cpCheckPointGreater(h0, h2, h1)){
hull2[count2] = hull[index];
count2++;
}
}
return EPARecurse(ctx, count2, hull2, iteration + 1);
} else {
// Could not find a new point to insert, so we have found the closest edge of the minkowski difference.
@@ -352,7 +352,7 @@ GJKRecurse(const struct SupportContext *ctx, const struct MinkowskiPoint v0, con
cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);
return ClosestPointsNew(v0, v1);
}
if(cpCheckPointGreater(v1.ab, v0.ab, cpvzero)){
// Origin is behind axis. Flip and try again.
return GJKRecurse(ctx, v1, v0, iteration);
@@ -360,15 +360,15 @@ GJKRecurse(const struct SupportContext *ctx, const struct MinkowskiPoint v0, con
cpFloat t = ClosestT(v0.ab, v1.ab);
cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(cpvsub(v1.ab, v0.ab)) : cpvneg(LerpT(v0.ab, v1.ab, t)));
struct MinkowskiPoint p = Support(ctx, n);
#if DRAW_GJK
ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1));
cpVect c = cpvlerp(v0.ab, v1.ab, 0.5);
ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1));
ChipmunkDebugDrawDot(5.0, p.ab, LAColor(1, 1));
#endif
if(cpCheckPointGreater(p.ab, v0.ab, cpvzero) && cpCheckPointGreater(v1.ab, p.ab, cpvzero)){
// The triangle v0, p, v1 contains the origin. Use EPA to find the MSA.
cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK->EPA iterations: %d", iteration);
@@ -419,27 +419,27 @@ GJK(const struct SupportContext *ctx, cpCollisionID *id)
#if DRAW_GJK || DRAW_EPA
int count1 = 1;
int count2 = 1;
switch(ctx->shape1->klass->type){
case CP_SEGMENT_SHAPE: count1 = 2; break;
case CP_POLY_SHAPE: count1 = ((cpPolyShape *)ctx->shape1)->count; break;
default: break;
}
switch(ctx->shape2->klass->type){
case CP_SEGMENT_SHAPE: count1 = 2; break;
case CP_POLY_SHAPE: count2 = ((cpPolyShape *)ctx->shape2)->count; break;
default: break;
}
// draw the minkowski difference origin
cpVect origin = cpvzero;
ChipmunkDebugDrawDot(5.0, origin, RGBAColor(1,0,0,1));
int mdiffCount = count1*count2;
cpVect *mdiffVerts = alloca(mdiffCount*sizeof(cpVect));
for(int i=0; i<count1; i++){
for(int j=0; j<count2; j++){
cpVect v = cpvsub(ShapePoint(ctx->shape2, j).p, ShapePoint(ctx->shape1, i).p);
@@ -447,13 +447,13 @@ GJK(const struct SupportContext *ctx, cpCollisionID *id)
ChipmunkDebugDrawDot(2.0, v, RGBAColor(1, 0, 0, 1));
}
}
cpVect *hullVerts = alloca(mdiffCount*sizeof(cpVect));
int hullCount = cpConvexHull(mdiffCount, mdiffVerts, hullVerts, NULL, 0.0);
ChipmunkDebugDrawPolygon(hullCount, hullVerts, 0.0, RGBAColor(1, 0, 0, 1), RGBAColor(1, 0, 0, 0.25));
#endif
struct MinkowskiPoint v0, v1;
if(*id){
// Use the minkowski points from the last frame as a starting point using the cached indexes.
@@ -465,7 +465,7 @@ GJK(const struct SupportContext *ctx, cpCollisionID *id)
v0 = Support(ctx, axis);
v1 = Support(ctx, cpvneg(axis));
}
struct ClosestPoints points = GJKRecurse(ctx, v0, v1, 1);
*id = points.id;
return points;
@@ -484,17 +484,17 @@ ContactPoints(const struct Edge e1, const struct Edge e2, const struct ClosestPo
ChipmunkDebugDrawFatSegment(e2.a.p, e2.b.p, e2.r, RGBAColor(1, 0, 0, 1), LAColor(0, 0));
#endif
cpVect n = info->n = points.n;
// Distances along the axis parallel to n
cpFloat d_e1_a = cpvcross(e1.a.p, n);
cpFloat d_e1_b = cpvcross(e1.b.p, n);
cpFloat d_e2_a = cpvcross(e2.a.p, n);
cpFloat d_e2_b = cpvcross(e2.b.p, n);
// TODO + min isn't a complete fix.
cpFloat e1_denom = 1.0f/(d_e1_b - d_e1_a + CPFLOAT_MIN);
cpFloat e2_denom = 1.0f/(d_e2_b - d_e2_a + CPFLOAT_MIN);
// Project the endpoints of the two edges onto the opposing edge, clamping them as necessary.
// Compare the projected points to the collision normal to see if the shapes overlap there.
{
@@ -528,7 +528,7 @@ CircleToCircle(const cpCircleShape *c1, const cpCircleShape *c2, struct cpCollis
cpFloat mindist = c1->r + c2->r;
cpVect delta = cpvsub(c2->tc, c1->tc);
cpFloat distsq = cpvlengthsq(delta);
if(distsq < mindist*mindist){
cpFloat dist = cpfsqrt(distsq);
cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : cpv(1.0f, 0.0f));
@@ -542,12 +542,12 @@ CircleToSegment(const cpCircleShape *circle, const cpSegmentShape *segment, stru
cpVect seg_a = segment->ta;
cpVect seg_b = segment->tb;
cpVect center = circle->tc;
// Find the closest point on the segment to the circle.
cpVect seg_delta = cpvsub(seg_b, seg_a);
cpFloat closest_t = cpfclamp01(cpvdot(seg_delta, cpvsub(center, seg_a))/cpvlengthsq(seg_delta));
cpVect closest = cpvadd(seg_a, cpvmult(seg_delta, closest_t));
// Compare the radii of the two shapes to see if they are colliding.
cpFloat mindist = circle->r + segment->r;
cpVect delta = cpvsub(closest, center);
@@ -556,7 +556,7 @@ CircleToSegment(const cpCircleShape *circle, const cpSegmentShape *segment, stru
cpFloat dist = cpfsqrt(distsq);
// Handle coincident shapes as gracefully as possible.
cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : segment->tn);
// Reject endcap collisions if tangents are provided.
cpVect rot = cpBodyGetRotation(segment->shape.body);
if(
@@ -573,22 +573,22 @@ SegmentToSegment(const cpSegmentShape *seg1, const cpSegmentShape *seg2, struct
{
struct SupportContext context = {(cpShape *)seg1, (cpShape *)seg2, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)SegmentSupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(6.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(6.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
cpVect n = points.n;
cpVect rot1 = cpBodyGetRotation(seg1->shape.body);
cpVect rot2 = cpBodyGetRotation(seg2->shape.body);
// If the closest points are nearer than the sum of the radii...
if(
points.d <= (seg1->r + seg2->r) && (
@@ -608,18 +608,18 @@ PolyToPoly(const cpPolyShape *poly1, const cpPolyShape *poly2, struct cpCollisio
{
struct SupportContext context = {(cpShape *)poly1, (cpShape *)poly2, (SupportPointFunc)PolySupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
// If the closest points are nearer than the sum of the radii...
if(points.d - poly1->r - poly2->r <= 0.0){
ContactPoints(SupportEdgeForPoly(poly1, points.n), SupportEdgeForPoly(poly2, cpvneg(points.n)), points, info);
@@ -631,21 +631,21 @@ SegmentToPoly(const cpSegmentShape *seg, const cpPolyShape *poly, struct cpColli
{
struct SupportContext context = {(cpShape *)seg, (cpShape *)poly, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
cpVect n = points.n;
cpVect rot = cpBodyGetRotation(seg->shape.body);
if(
// If the closest points are nearer than the sum of the radii...
points.d - seg->r - poly->r <= 0.0 && (
@@ -663,14 +663,14 @@ CircleToPoly(const cpCircleShape *circle, const cpPolyShape *poly, struct cpColl
{
struct SupportContext context = {(cpShape *)circle, (cpShape *)poly, (SupportPointFunc)CircleSupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
// If the closest points are nearer than the sum of the radii...
if(points.d <= circle->r + poly->r){
cpVect n = info->n = points.n;
@@ -702,25 +702,25 @@ struct cpCollisionInfo
cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts)
{
struct cpCollisionInfo info = {a, b, id, cpvzero, 0, contacts};
// Make sure the shape types are in order.
if(a->klass->type > b->klass->type){
info.a = b;
info.b = a;
}
CollisionFuncs[info.a->klass->type + info.b->klass->type*CP_NUM_SHAPES](info.a, info.b, &info);
// if(0){
// for(int i=0; i<info.count; i++){
// cpVect r1 = info.arr[i].r1;
// cpVect r2 = info.arr[i].r2;
// cpVect mid = cpvlerp(r1, r2, 0.5f);
//
//
// ChipmunkDebugDrawSegment(r1, mid, RGBAColor(1, 0, 0, 1));
// ChipmunkDebugDrawSegment(r2, mid, RGBAColor(0, 0, 1, 1));
// }
// }
return info;
}

View File

@@ -1,15 +1,15 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
*
* 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
@@ -42,23 +42,23 @@ cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform)
int count = poly->count;
struct cpSplittingPlane *dst = poly->planes;
struct cpSplittingPlane *src = dst + count;
cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY;
cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY;
for(int i=0; i<count; i++){
cpVect v = cpTransformPoint(transform, src[i].v0);
cpVect n = cpTransformVect(transform, src[i].n);
dst[i].v0 = v;
dst[i].n = n;
l = cpfmin(l, v.x);
r = cpfmax(r, v.x);
b = cpfmin(b, v.y);
t = cpfmax(t, v.y);
}
cpFloat radius = poly->r;
return (poly->shape.bb = cpBBNew(l - radius, b - radius, r + radius, t + radius));
}
@@ -68,36 +68,36 @@ cpPolyShapePointQuery(cpPolyShape *poly, cpVect p, cpPointQueryInfo *info){
int count = poly->count;
struct cpSplittingPlane *planes = poly->planes;
cpFloat r = poly->r;
cpVect v0 = planes[count - 1].v0;
cpFloat minDist = INFINITY;
cpVect closestPoint = cpvzero;
cpVect closestNormal = cpvzero;
cpBool outside = cpFalse;
for(int i=0; i<count; i++){
cpVect v1 = planes[i].v0;
outside = outside || (cpvdot(planes[i].n, cpvsub(p,v1)) > 0.0f);
cpVect closest = cpClosetPointOnSegment(p, v0, v1);
cpFloat dist = cpvdist(p, closest);
if(dist < minDist){
minDist = dist;
closestPoint = closest;
closestNormal = planes[i].n;
}
v0 = v1;
}
cpFloat dist = (outside ? minDist : -minDist);
cpVect g = cpvmult(cpvsub(p, closestPoint), 1.0f/dist);
info->shape = (cpShape *)poly;
info->point = cpvadd(closestPoint, cpvmult(g, r));
info->distance = dist - r;
// Use the normal of the closest segment if the distance is small.
info->gradient = (minDist > MAGIC_EPSILON ? g : closestNormal);
}
@@ -109,23 +109,23 @@ cpPolyShapeSegmentQuery(cpPolyShape *poly, cpVect a, cpVect b, cpFloat r2, cpSeg
int count = poly->count;
cpFloat r = poly->r;
cpFloat rsum = r + r2;
for(int i=0; i<count; i++){
cpVect n = planes[i].n;
cpFloat an = cpvdot(a, n);
cpFloat d = an - cpvdot(planes[i].v0, n) - rsum;
if(d < 0.0f) continue;
cpFloat bn = cpvdot(b, n);
// Avoid divide by zero. (d is always positive)
cpFloat t = d/cpfmax(an - bn, CPFLOAT_MIN);
if(t < 0.0f || 1.0f < t) continue;
cpVect point = cpvlerp(a, b, t);
cpFloat dt = cpvcross(n, point);
cpFloat dtMin = cpvcross(n, planes[(i - 1 + count)%count].v0);
cpFloat dtMax = cpvcross(n, planes[i].v0);
if(dtMin <= dt && dt <= dtMax){
info->shape = (cpShape *)poly;
info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2));
@@ -133,7 +133,7 @@ cpPolyShapeSegmentQuery(cpPolyShape *poly, cpVect a, cpVect b, cpFloat r2, cpSeg
info->alpha = t;
}
}
// Also check against the beveled vertexes.
if(rsum > 0.0f){
for(int i=0; i<count; i++){
@@ -153,12 +153,12 @@ SetVerts(cpPolyShape *poly, int count, const cpVect *verts)
} else {
poly->planes = (struct cpSplittingPlane *)cpcalloc(2*count, sizeof(struct cpSplittingPlane));
}
for(int i=0; i<count; i++){
cpVect a = verts[(i - 1 + count)%count];
cpVect b = verts[i];
cpVect n = cpvnormalize(cpvrperp(cpvsub(b, a)));
poly->planes[i + count].v0 = b;
poly->planes[i + count].n = n;
}
@@ -168,14 +168,14 @@ static struct cpShapeMassInfo
cpPolyShapeMassInfo(cpFloat mass, int count, const cpVect *verts, cpFloat radius)
{
// TODO moment is approximate due to radius.
cpVect centroid = cpCentroidForPoly(count, verts);
struct cpShapeMassInfo info = {
mass, cpMomentForPoly(1.0f, count, verts, cpvneg(centroid), radius),
centroid,
cpAreaForPoly(count, verts, radius),
};
return info;
}
@@ -191,10 +191,10 @@ cpPolyShape *
cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius)
{
cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));
// Transform the verts before building the hull in case of a negative scale.
for(int i=0; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);
unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);
return cpPolyShapeInitRaw(poly, body, hullCount, hullVerts, radius);
}
@@ -203,7 +203,7 @@ cpPolyShape *
cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius)
{
cpShapeInit((cpShape *)poly, &polyClass, body, cpPolyShapeMassInfo(0.0f, count, verts, radius));
SetVerts(poly, count, verts);
poly->r = radius;
@@ -227,20 +227,20 @@ cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, c
{
cpFloat hw = width/2.0f;
cpFloat hh = height/2.0f;
return cpBoxShapeInit2(poly, body, cpBBNew(-hw, -hh, hw, hh), radius);
}
cpPolyShape *
cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius)
{
cpVect verts[] = {
cpVect verts[4] = {
cpv(box.r, box.b),
cpv(box.r, box.t),
cpv(box.l, box.t),
cpv(box.l, box.b),
};
return cpPolyShapeInitRaw(poly, body, 4, verts, radius);
}
@@ -267,10 +267,10 @@ cpVect
cpPolyShapeGetVert(const cpShape *shape, int i)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
int count = cpPolyShapeGetCount(shape);
cpAssertHard(0 <= i && i < count, "Index out of range.");
return ((cpPolyShape *)shape)->planes[i + count].v0;
}
@@ -287,10 +287,10 @@ void
cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform)
{
cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));
// Transform the verts before building the hull in case of a negative scale.
for(int i=0; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);
unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);
cpPolyShapeSetVertsRaw(shape, hullCount, hullVerts);
}
@@ -301,9 +301,9 @@ cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts)
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
cpPolyShape *poly = (cpPolyShape *)shape;
cpPolyShapeDestroy(poly);
SetVerts(poly, count, verts);
cpFloat mass = shape->massInfo.m;
shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, count, verts, poly->r);
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
@@ -315,8 +315,8 @@ cpPolyShapeSetRadius(cpShape *shape, cpFloat radius)
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
cpPolyShape *poly = (cpPolyShape *)shape;
poly->r = radius;
// TODO radius is not handled by moment/area
// cpFloat mass = shape->massInfo.m;
// shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, poly->count, poly->verts, poly->r);

View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2017 Simon Schoenenberger
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.

View File

@@ -1,123 +0,0 @@
# Bitmap Outliner
This algorithm converts a bitmap image to vector paths enclosing the pixel groups.
![Conversion Diagram](assets/conversion-diagram.svg)
*The outlined paths on the right side are slightly shifted to show their way around the pixels; they will, of course, be aligned with the pixel borders.*
## SVG Paths
Given the following bitmap from the above image:
```c
0, 1, 1, 1, 0, 0,
1, 0, 1, 0, 0, 1,
1, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 1,
0, 0, 1, 0, 1, 1,
1, 0, 1, 1, 1, 0,
```
The generated SVG path will look like this (line breaks are added to separate the path loops):
```xml
<svg viewBox="0 0 6 6" xmlns="http://www.w3.org/2000/svg">
<path d="
M 1 0h3v1h-1v1h-1v-1h-1v1h1v1h-1v1h-1v-3h1z
M 5 1h1v4h-1v1h-3v-2h1v1h1v-1h1v-1h-1v1h-1v-1h1v-1h1z
M 0 5h1v1h-1z"
fill="#000" fill-rule="evenodd"/>
</svg>
```
## C Example
```c
#include <stdio.h>
#include "bitmap-outliner.h"
// the bitmap size
int const width = 6;
int const height = 6;
// the bitmap data
uint8_t const data[] = {
0, 1, 1, 1, 0, 0,
1, 0, 1, 0, 0, 1,
1, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 1,
0, 0, 1, 0, 1, 1,
1, 0, 1, 1, 1, 0,
};
int main() {
// allocate outliner
bmol_outliner* outliner = bmol_alloc(width, height, data);
// find paths in bitmap
bmol_find_paths(outliner, NULL);
// calculate SVG path length (needs some performance).
// for numerous calls to `bmol_svg_path`,
// better use a large enough buffer directly.
size_t path_len = bmol_svg_path_len(outliner);
// ok for small bitmaps; be aware to not use large buffers on the stack!
char path[path_len];
// write SVG path to `path`
bmol_svg_path(outliner, path, path_len);
// output SVG
printf(
"<svg viewBox=\"0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n"
" <path d=\"%s\" fill=\"#000\" fill-rule=\"evenodd\"/>\n"
"</svg>\n", width, height, path);
// free outliner
bmol_free(outliner);
return 0;
}
```
### Run
Execute the following command to run `main.c`:
```sh
$ sh main.c
```
## Javascript Example
```js
import 'src/bitmap-outliner';
// the bitmap size
const width = 6;
const height = 6;
// data can be any indexable array
const data = new Uint8Array([
0, 1, 1, 1, 0, 0,
1, 0, 1, 0, 0, 1,
1, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 1,
0, 0, 1, 0, 1, 1,
1, 0, 1, 1, 1, 0,
]);
// create outliner
let outliner = new BitmapOutliner(width, height, data);
// get SVG path; implicitly calls `outliner.findPaths()`
let path = outliner.svgPath();
// output SVG
console.log(
`<svg viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg">
<path d="${path}" fill="#000" fill-rule="evenodd"/>
</svg>`);
```

View File

@@ -1,77 +0,0 @@
#include <stdio.h>
#include "bitmap-outliner.h"
/**
* Terminal colors.
*/
enum {
COLOR_RESET = 0,
COLOR_RED,
COLOR_GREEN,
COLOR_YELLOW,
};
/**
* Terminal color escape sequences.
*/
static char const* const colors[] = {
[COLOR_RESET] = "\033[0m",
[COLOR_RED] = "\033[31m",
[COLOR_GREEN] = "\033[32m",
[COLOR_YELLOW] = "\033[33m",
};
/**
* Print arrow grid.
*
* @param width Width of bitmap.
* @param height Height of bitmap.
* @param data The bitmap to print.
* @param grid The arrow grid to print.
*/
static void print_grid(int width, int height, uint8_t const data[height][width], bmol_arrow const grid[height * 2 + 3][width + 3]) {
static char const* arrows[] = {
[BMOL_ARR_NONE] = "",
[BMOL_ARR_RIGHT] = "",
[BMOL_ARR_LEFT] = "",
[BMOL_ARR_DOWN] = "",
[BMOL_ARR_UP] = "",
};
int gridWidth = width + 3;
int gridHeight = height * 2 + 3;
for (int y = 0; y < gridHeight; y++) {
if (y % 2 != 0) {
printf(" ");
}
for (int x = 0; x < gridWidth - (y % 2 != 0); x++) {
bmol_arrow a = grid[y][x];
int type = a.type;
char const* color = "";
if (type) {
color = colors[a.inner ? COLOR_RED : COLOR_GREEN];
}
printf("%s%s%s", color, arrows[(int)type], colors[COLOR_RESET]);
if (x > 0 && y >= 2 && x < gridWidth - 2 && y < gridHeight - 2 && (y % 2) == 0) {
printf(" %c ", data[(y - 2) / 2][x - 1] ? '#' : ' ');
}
else {
printf(" ");
}
}
printf("\n");
}
}
void bmol_print_grid(bmol_outliner const* outliner) {
int width = outliner->width;
int height = outliner->height;
print_grid(width, height, (uint8_t const (*)[width])outliner->data, (bmol_arrow (*)[width])outliner->arrow_grid);
}

View File

@@ -1,628 +0,0 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "bitmap-outliner.h"
#define MIN_SEGMENTS_COUNT 64
/**
* Information about how to proceed to next arrow.
*/
typedef struct {
bmol_arr_type arrow:8; ///< Type of arrow.
int8_t dx; ///< Relative to current position.
int8_t dy; ///< Relative to current position.
} const arrow_next;
/**
* String buffer context.
*/
typedef struct {
char* buffer; ///< A string buffer.
size_t buf_size; ///< The remaining buffer size
size_t size; ///< The buffer size.
} buffer_ctx;
/**
* The arrow states.
*/
static arrow_next const states[][2][4] = {
[BMOL_ARR_RIGHT] = {
{
{BMOL_ARR_LEFT, +1, 0}, {BMOL_ARR_UP, +1, -1},
{BMOL_ARR_RIGHT, +1, 0}, {BMOL_ARR_DOWN, +1, +1},
}, {
{BMOL_ARR_DOWN, +1, +1}, {BMOL_ARR_DOWN, +1, +1},
{BMOL_ARR_RIGHT, +1, 0}, {BMOL_ARR_UP, +1, -1},
},
},
[BMOL_ARR_LEFT] = {
{
{BMOL_ARR_RIGHT, -1, 0}, {BMOL_ARR_DOWN, 0, +1},
{BMOL_ARR_LEFT, -1, 0}, {BMOL_ARR_UP, 0, -1},
}, {
{BMOL_ARR_UP, 0, -1}, {BMOL_ARR_UP, 0, -1},
{BMOL_ARR_LEFT, -1, 0}, {BMOL_ARR_DOWN, 0, +1},
},
},
[BMOL_ARR_DOWN] = {
{
{BMOL_ARR_UP, 0, +2}, {BMOL_ARR_RIGHT, 0, +1},
{BMOL_ARR_DOWN, 0, +2}, {BMOL_ARR_LEFT, -1, +1}
}, {
{BMOL_ARR_LEFT, -1, +1}, {BMOL_ARR_LEFT, -1, +1},
{BMOL_ARR_DOWN, 0, +2}, {BMOL_ARR_RIGHT, 0, +1},
},
},
[BMOL_ARR_UP] = {
{
{BMOL_ARR_DOWN, 0, -2}, {BMOL_ARR_LEFT, -1, -1},
{BMOL_ARR_UP, 0, -2}, {BMOL_ARR_RIGHT, 0, -1},
}, {
{BMOL_ARR_RIGHT, 0, -1}, {BMOL_ARR_RIGHT, 0, -1},
{BMOL_ARR_UP, 0, -2}, {BMOL_ARR_LEFT, -1, -1},
},
},
};
/**
* Convert arrow grid coordinates to path coordinates.
*
* @param type Arrow type.
* @param gx Grid X-coordinate.
* @param gy Grid Y-coordinate.
* @param rx Path X-coordinates.
* @param ry Path Y-coordinates.
*/
static void real_coords(bmol_arr_type type, int gx, int gy, int* rx, int* ry) {
/**
* Defines coordinates.
*/
typedef struct {
int8_t x; ///< X-coordinate.
int8_t y; ///< Y-coordinate.
} coords;
/**
* Delta to add to convert to real coordinates.
*/
static coords const real_delta[] = {
[BMOL_ARR_RIGHT] = {0, 0},
[BMOL_ARR_LEFT] = {1, 0},
[BMOL_ARR_DOWN] = {0, 0},
[BMOL_ARR_UP] = {0, 1},
};
coords const* real = &real_delta[type];
*rx = gx - 1 + real->x;
*ry = (gy - 1) / 2 + real->y;
}
/**
* Allocate more segments.
*
* @param outliner The outline object.
* @return 0 on success.
*/
static int grow_segments(bmol_outliner* outliner) {
bmol_path_seg* segments = outliner->segments;
int segments_cap = outliner->segments_cap * 2 + 1;
if (segments_cap < MIN_SEGMENTS_COUNT) {
segments_cap = MIN_SEGMENTS_COUNT;
}
segments = realloc(segments, sizeof(*segments) * segments_cap);
if (!segments) {
return -1;
}
outliner->segments = segments;
outliner->segments_cap = segments_cap;
return 0;
}
/**
* Add segment to segment list.
*
* @param outliner The outline object.
* @param type The arrow type.
* @param dx Path segment X-coordinate.
* @param dy Path segment Y-coordinate.
* @return 0 on success.
*/
static int push_segment(bmol_outliner* outliner, bmol_arr_type type, int dx, int dy) {
bmol_path_seg* segments = outliner->segments;
if (outliner->segments_size >= outliner->segments_cap) {
if (grow_segments(outliner) < 0) {
return -1;
}
segments = outliner->segments;
}
bmol_path_seg* segment = &segments[outliner->segments_size++];
segment->type = type;
segment->dx = dx;
segment->dy = dy;
return 0;
}
/**
* Search adjacent arrow relative to given position.
*
* @param width Width of bitmap.
* @param height Height of bitmap.
* @param grid Arrow grid.
* @param type Current arrow type.
* @param inner Is inner path.
* @param xd Arrow X-coordinate.
* @param yd Arrow Y-coordinate.
*/
static bmol_arrow* search_adjacent_arrow(int width, int height, bmol_arrow grid[height * 2 + 3][width + 3], bmol_arr_type type, int inner, int* xd, int* yd) {
arrow_next* arrows = &states[type][inner][0];
// search for adjacent arrows in precedence order
for (int n = 0; n < 4; n++) {
arrow_next const* search = &arrows[n];
int const xn = *xd + search->dx;
int const yn = *yd + search->dy;
bmol_arrow* nextArrow = &grid[yn][xn];
// follow adjacent arrow
if (nextArrow->type == search->arrow && !nextArrow->seen) {
// is opposite arrow
if (n == 0) {
if (!inner && nextArrow->inner) {
*xd = xn;
*yd = yn;
nextArrow->seen = 0; // do not mark opposite arrow as seen
type = nextArrow->type;
// search next inner arrow relative to opposite arrow
return search_adjacent_arrow(width, height, grid, type, 1, xd, yd);
}
else {
continue;
}
}
// ignore arrows not in path type
else if (nextArrow->inner != inner) {
continue;
}
*xd = xn;
*yd = yn;
return nextArrow;
}
}
// switch to outer path if no more inner path arrows found
if (inner) {
return search_adjacent_arrow(width, height, grid, type, 0, xd, yd);
}
return NULL;
}
/**
* Make path segments.
*
* @param x First path arrow.
* @param y First path arrow.
* @param width Width of bitmap.
* @param height Height of bitmap.
* @param grid Grid to search for paths.
*/
static int make_path(bmol_outliner* outliner, int x, int y, int width, int height, bmol_arrow grid[height * 2 + 3][width + 3]) {
int xd = x;
int yd = y;
int xr, yr;
int xp, yp;
bmol_arrow* arrow = &grid[yd][xd];
bmol_arrow* nextArrow = arrow;
bmol_arr_type type = arrow->type;
int inner = (type == BMOL_ARR_LEFT);
bmol_arr_type prevType = type;
real_coords(type, xd, yd, &xr, &yr);
xp = xr;
yp = yr;
// begin path
if (push_segment(outliner, BMOL_ARR_NONE, xr, yr) != 0) {
return -1;
}
do {
arrow = nextArrow;
arrow->seen = 1; // mark as seen
nextArrow = search_adjacent_arrow(width, height, grid, type, inner, &xd, &yd);
type = BMOL_ARR_NONE;
if (nextArrow) {
type = nextArrow->type;
inner = nextArrow->inner; // switch arrow type
}
// end path segment if arrow changes
// and ignore last path segment
if (type != prevType && type) {
int dx, dy;
real_coords(type, xd, yd, &xr, &yr);
dx = xr - xp;
dy = yr - yp;
xp = xr;
yp = yr;
// add path segment
if (push_segment(outliner, prevType, dx, dy) < 0) {
return-1;
}
prevType = type;
}
}
while (type);
return 0;
}
/**
* Mark arrow as outer and inner.
*
* @param x First path arrow.
* @param y First path arrow.
* @param width Width of bitmap.
* @param height Height of bitmap.
* @param grid Grid to search for paths.
*/
static void set_path_type(bmol_outliner* outliner, int x, int y, int width, int height, bmol_arrow grid[height * 2 + 3][width + 3]) {
bmol_arrow* arrow = &grid[y][x];
bmol_arr_type type = arrow->type;
int const inner = (type == BMOL_ARR_LEFT);
do {
arrow_next* arrows = &states[type][inner][1]; // ignore opponent arrow
// mark as visited
arrow->visited = 1;
arrow->inner = inner;
type = BMOL_ARR_NONE;
// search for adjacent arrows in precedence order
for (int n = 0; n < 3; n++) {
arrow_next* search = &arrows[n];
int xn = x + search->dx;
int yn = y + search->dy;
bmol_arrow* nextArrow = &grid[yn][xn];
// follow adjacent arrow
if (nextArrow->type == search->arrow && !nextArrow->visited) {
x = xn;
y = yn;
type = nextArrow->type;
arrow = nextArrow;
break;
}
}
}
while (type);
}
/**
* Search all paths in arrow grid.
*
* @param width Width of bitmap.
* @param height Height of bitmap.
* @param grid Grid to search for paths.
*/
static int search_paths(bmol_outliner* outliner, int width, int height, bmol_arrow grid[height * 2 + 3][width + 3]) {
int const gridWidth = width + 3;
int const gridHeight = height * 2 + 3;
// set arrow types
for (int y = 1; y < gridHeight - 1; y += 2) {
for (int x = 1; x < gridWidth - 1; x++) {
bmol_arrow arrow = grid[y][x];
if (arrow.type && !arrow.visited) {
set_path_type(outliner, x, y, width, height, grid);
}
}
}
// search right and left arrows in grid
for (int y = 1; y < gridHeight - 1; y += 2) {
for (int x = 1; x < gridWidth - 1; x++) {
bmol_arrow arrow = grid[y][x];
if (arrow.type && !arrow.seen) {
if (make_path(outliner, x, y, width, height, grid) < 0) {
return -1;
}
}
}
}
return 0;
}
/**
* Fill arrow grid.
*
* @param width Width of bitmap.
* @param height Height of bitmap.
* @param map The bitmap.
* @param grid Grid to fill with arrows.
*/
static void set_arrows(int width, int height, uint8_t const map[height][width], bmol_arrow grid[height * 2 + 3][width + 3]) {
int x, y, t;
for (x = 0; x < width; x++) {
for (y = 0, t = 0; y < height; y++) {
int p = map[y][x] != 0;
if (p != t) {
grid[y * 2 + 1][x + 1].type = t ? BMOL_ARR_LEFT : BMOL_ARR_RIGHT;
t = p;
}
}
if (map[y - 1][x]) {
grid[y * 2 + 1][x + 1].type = BMOL_ARR_LEFT;
}
}
for (y = 0; y < height; y++) {
for (x = 0, t = 0; x < width; x++) {
int p = map[y][x] != 0;
if (p != t) {
grid[y * 2 + 2][x + 1].type = t ? BMOL_ARR_DOWN : BMOL_ARR_UP;
t = p;
}
}
if (map[y][x - 1]) {
grid[y * 2 + 2][x + 1].type = BMOL_ARR_DOWN;
}
}
}
bmol_outliner* bmol_alloc(int width, int height, uint8_t const* data) {
bmol_outliner* outliner;
size_t const size = (width + 3) * (height * 2 + 3) * sizeof(outliner->arrow_grid[0]);
outliner = calloc(1, sizeof(*outliner) + size);
if (!outliner) {
return NULL;
}
outliner->width = width;
outliner->height = height;
outliner->data = data;
if (grow_segments(outliner) != 0) {
bmol_free(outliner);
return NULL;
}
return outliner;
}
void bmol_free(bmol_outliner* outliner) {
free(outliner->segments);
free(outliner);
}
bmol_path_seg const* bmol_find_paths(bmol_outliner* outliner, int* out_size) {
int const width = outliner->width;
int const height = outliner->height;
bmol_arrow* grid = outliner->arrow_grid;
uint8_t const* data = outliner->data;
if (!data) {
return NULL;
}
outliner->segments_size = 0;
set_arrows(width, height, (const uint8_t (*)[width])data, (bmol_arrow (*)[width])grid);
if (search_paths(outliner, width, height, (bmol_arrow (*)[width])grid) < 0) {
return NULL;
}
if (out_size) {
*out_size = outliner->segments_size;
}
return outliner->segments;
}
/**
* Append formated string to buffer.
*
* @param buffer_ref A reference to the buffer.
* @param size_ref A reference to the buffer size.
* @param format The number format.
*/
static void append_string(buffer_ctx* ctx, char const* format, ...) {
int size;
va_list args;
va_start(args, format);
size = vsnprintf(ctx->buffer, ctx->buf_size, format, args);
va_end(args);
if (size > 0) {
if (size >= ctx->buf_size) {
size = ctx->buf_size - 1;
}
ctx->buf_size -= size;
ctx->buffer += size;
ctx->size += size;
}
}
/**
* Calculate log2 of an integer.
*
* https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
*
* @param n The value to get the log2 from.
* @return log2 of the given integer.
*/
static uint32_t log2_fast(uint32_t n) {
uint32_t r;
uint32_t shift;
r = (n > 0xFFFF) << 4; n >>= r;
shift = (n > 0xFF ) << 3; n >>= shift; r |= shift;
shift = (n > 0xF ) << 2; n >>= shift; r |= shift;
shift = (n > 0x3 ) << 1; n >>= shift; r |= shift;
r |= (n >> 1);
return r;
}
/**
* Calculate log10 of an integer.
*
* https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
*
* @param n The value to get the log10 from.
* @return log10 of the given integer.
*/
static int log10_fast(uint32_t n) {
static uint32_t const pow_10[] = {
1, 10, 100, 1000, 10000, 100000,
1000000, 10000000, 100000000, 1000000000
};
int t = (log2_fast(n) + 1) * 1233 >> 12;
int r = t - (n < pow_10[t]);
return r;
}
/**
* Write SVG path to string buffer.
*
* @param segments The path segments.
* @param count The number of path segments.
* @param buffer_ref A reference to the buffer.
* @param size_ref A reference to the buffer size.
*/
static void write_svg(bmol_path_seg const* segments, int count, buffer_ctx* ctx) {
for (int i = 0; i < count; i++) {
bmol_path_seg const* segment = &segments[i];
switch (segment->type) {
case BMOL_ARR_NONE: {
if (i > 0) {
append_string(ctx, "z");
}
append_string(ctx, "M%d,%d", segment->dx, segment->dy);
break;
}
case BMOL_ARR_RIGHT:
case BMOL_ARR_LEFT: {
append_string(ctx, "h%d", segment->dx);
break;
}
case BMOL_ARR_DOWN:
case BMOL_ARR_UP: {
append_string(ctx, "v%d", segment->dy);
break;
}
default: {
break;
}
}
}
append_string(ctx, "z");
}
size_t bmol_svg_path_len(bmol_outliner* outliner) {
int len = 0;
for (int i = 0; i < outliner->segments_size; i++) {
bmol_path_seg const* segment = &outliner->segments[i];
int dx = segment->dx;
int dy = segment->dy;
switch (segment->type) {
case BMOL_ARR_NONE: {
len += 4 + log10_fast(dx > 0 ? dx : 1) + 1 + log10_fast(dy > 0 ? dy : 1) + 1;
break;
}
case BMOL_ARR_RIGHT:
case BMOL_ARR_LEFT:
case BMOL_ARR_DOWN:
case BMOL_ARR_UP: {
if (dx < 0) {
dx = -dx;
len++;
}
if (dx > 0) {
len += 1 + log10_fast(dx) + 1;
}
if (dy < 0) {
dy = -dy;
len++;
}
if (dy > 0) {
len += 1 + log10_fast(dy) + 1;
}
break;
}
default: {
break;
}
}
}
return len + 1;
}
size_t bmol_svg_path(bmol_outliner* outliner, char buffer[], size_t buf_size) {
buffer_ctx ctx = {
.buffer = buffer,
.buf_size = buf_size,
.size = 0,
};
write_svg(outliner->segments, outliner->segments_size, &ctx);
return ctx.size;
}
void bmol_set_bitmap(bmol_outliner* outliner, uint8_t const* data) {
outliner->data = data;
}

View File

@@ -1,10 +0,0 @@
#pragma once
#include "bitmap-outliner.h"
/**
* Print arrow grid
*
* @param The outliner object.
*/
extern void bmol_print_grid(bmol_outliner const* outliner);

View File

@@ -1,98 +0,0 @@
#pragma once
#include <stdint.h>
#include <sys/types.h>
/**
* Arrow types.
*/
typedef enum {
BMOL_ARR_NONE = 0,
BMOL_ARR_RIGHT,
BMOL_ARR_LEFT,
BMOL_ARR_DOWN,
BMOL_ARR_UP
} bmol_arr_type;
/**
* Defines arrow.
*/
typedef struct {
uint8_t type:3; ///< Arrow type.
uint8_t inner:1; ///< Associated path is inner path.
uint8_t seen:1; ///< Has been seen.
uint8_t visited:1; ///< Has been visited.
} bmol_arrow;
/**
* Defines path segment.
*/
typedef struct {
uint8_t type; ///< Arrow type.
int dx; ///< Path segment width.
int dy; ///< Path segment height.
} bmol_path_seg;
/**
* Defines outliner object.
*/
typedef struct {
int width; ///< Bitmap width.
int height; ///< Bitmap height.
uint8_t const* data; ///< Bitmap data.
bmol_path_seg* segments; ///< Path segment buffer.
int segments_size; ///< Path segment buffer length.
int segments_cap; ///< Path segment buffer capacity.
bmol_arrow arrow_grid[]; ///< Grid arrows.
} bmol_outliner;
/**
* Allocate outliner object.
*
* @param width The bitmap width.
* @param height The bitmap height.
* @param data The bitmap data.
* @return Outliner object on success.
*/
extern bmol_outliner* bmol_alloc(int width, int height, uint8_t const* data);
/**
* Free outliner object.
*
* @param outliner The outline object.
*/
extern void bmol_free(bmol_outliner* outliner);
/**
* Find paths in bitmap data.
*
* @param outliner The outliner object.
* @param out_size The number of path fragments.
* @return The path fragments.
*/
extern bmol_path_seg const* bmol_find_paths(bmol_outliner* outliner, int* out_size);
/**
* Calculate the SVG path length.
*
* @return The length of the SVG path string.
*/
extern size_t bmol_svg_path_len(bmol_outliner* outliner);
/**
* Create SVG path from segments.
*
* @param outliner The outline object.
* @param buffer The buffer to write the SVG path into.
* @param buf_size The buffer capacity.
* @return The length of the generated SVG path.
*/
extern size_t bmol_svg_path(bmol_outliner* outliner, char buffer[], size_t buf_size);
/**
* Set bitmap data. Must have the same dimensions as the current one.
*
* @param outliner The outline robject.
* @param data The new bitmap data.
*/
extern void bmol_set_bitmap(bmol_outliner* outliner, uint8_t const* data);

View File

@@ -1 +0,0 @@
#define HAVE_COMPLEX_NUMBERS 0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -174,6 +174,8 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#define STBI_NO_SIMD
#endif
#define STBI_NO_SIMD
#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
#define STBI_SSE2
#include <emmintrin.h>