Large refactor - IMGUI to Nuklear
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
20
source/engine/thirdparty/bitmap-outliner/LICENSE
vendored
20
source/engine/thirdparty/bitmap-outliner/LICENSE
vendored
@@ -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.
|
||||
123
source/engine/thirdparty/bitmap-outliner/README.md
vendored
123
source/engine/thirdparty/bitmap-outliner/README.md
vendored
@@ -1,123 +0,0 @@
|
||||
# Bitmap Outliner
|
||||
|
||||
This algorithm converts a bitmap image to vector paths enclosing the pixel groups.
|
||||
|
||||

|
||||
|
||||
*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>`);
|
||||
```
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -1 +0,0 @@
|
||||
#define HAVE_COMPLEX_NUMBERS 0
|
||||
1452
source/engine/thirdparty/s7/include/s7.h
vendored
1452
source/engine/thirdparty/s7/include/s7.h
vendored
File diff suppressed because it is too large
Load Diff
114091
source/engine/thirdparty/s7/s7.c
vendored
114091
source/engine/thirdparty/s7/s7.c
vendored
File diff suppressed because it is too large
Load Diff
10552
source/engine/thirdparty/s7/s7.html
vendored
10552
source/engine/thirdparty/s7/s7.html
vendored
File diff suppressed because it is too large
Load Diff
2
source/engine/thirdparty/stb/stb_image.c
vendored
2
source/engine/thirdparty/stb/stb_image.c
vendored
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user