summaryrefslogtreecommitdiff
path: root/src/nanovg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/nanovg.cpp')
-rw-r--r--src/nanovg.cpp188
1 files changed, 126 insertions, 62 deletions
diff --git a/src/nanovg.cpp b/src/nanovg.cpp
index 5627b5f..1fb59ee 100644
--- a/src/nanovg.cpp
+++ b/src/nanovg.cpp
@@ -1,23 +1,23 @@
-enum nvg_point_flags
-{
- NVG_PT_CORNER = 0x01,
- NVG_PT_LEFT = 0x02,
- NVG_PT_BEVEL = 0x04,
- NVG_PR_INNERBEVEL = 0x08,
-};
+// nanovg code adapted by me for my data structures, I'm primarily using the
+// functions related to shape/path triangulation.
-struct nvg_point
-{
- real32 x;
- real32 y;
- real32 dx;
- real32 dy;
- real32 Length;
- real32 dmx;
- real32 dmy;
- uint8 Flags;
-};
+// Copyright (c) 2013 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
static real32
NVG_Normalize(real32 *x, float* y)
@@ -87,7 +87,30 @@ static real32 * NVG_RoundJoin(nvg_point *Point, nvg_point *NextPoint, real32 *St
StrokeData = NVG_Point(StrokeData, lx1, ly1, 0, 0);
StrokeData = NVG_Point(StrokeData, NextPoint->x - dlx1*rw, NextPoint->y - dly1*rw, 0, 0);
- }
+ } else {
+ float rx0,ry0,rx1,ry1,a0,a1;
+ NVG_ChooseBevel(NextPoint->Flags & NVG_PR_INNERBEVEL, Point, NextPoint, -rw, &rx0,&ry0, &rx1,&ry1);
+ a0 = atan2f(dly0, dlx0);
+ a1 = atan2f(dly1, dlx1);
+ if (a1 < a0) a1 += PI*2;
+
+ StrokeData = NVG_Point(StrokeData, NextPoint->x + dlx0*rw, NextPoint->y + dly0*rw, 0, 0);
+ StrokeData = NVG_Point(StrokeData, rx0, ry0, 0, 0);
+
+ n = NVG_Clampi((int)ceilf(((a1 - a0) / PI) * ncap), 2, ncap);
+ for (i = 0; i < n; i++) {
+ float u = i/(float)(n-1);
+ float a = a0 + u*(a1-a0);
+ float lx = NextPoint->x + cosf(a) * lw;
+ float ly = NextPoint->y + sinf(a) * lw;
+ StrokeData = NVG_Point(StrokeData, lx, ly, 0, 0);
+ StrokeData = NVG_Point(StrokeData, NextPoint->x, NextPoint->y, 0, 0);
+ }
+
+ StrokeData = NVG_Point(StrokeData, NextPoint->x + dlx1*rw, NextPoint->y + dly1*rw, 0, 0);
+ StrokeData = NVG_Point(StrokeData, rx1, ry1, 0, 0);
+ }
+
return StrokeData;
}
@@ -125,71 +148,100 @@ static real32 * NVG_RoundCap(nvg_point * Point, real32 *StrokeData,
return StrokeData;
}
+static real32 * NVG_ButtCap(nvg_point *Point, real32 *StrokeData,
+ float dx, float dy, float w, float d,
+ float u0, float u1, int Mode)
+{
+ float px = (Mode == 0) ? Point->x - dx*d : Point->x + dx*d;
+ float py = (Mode == 0) ? Point->y - dy*d : Point->y + dy*d;
+ float dlx = dy;
+ float dly = -dx;
+ if (Mode == 0) {
+ StrokeData = NVG_Point(StrokeData, px + dlx*w - dx, py + dly*w - dy, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px - dlx*w - dx, py - dly*w - dy, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px + dlx*w, py + dly*w, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px - dlx*w, py - dly*w, 0, 0);
+ } else {
+ StrokeData = NVG_Point(StrokeData, px + dlx*w, py + dly*w, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px - dlx*w, py - dly*w, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px + dlx*w + dx, py + dly*w + dy, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px - dlx*w + dx, py - dly*w + dy, 0, 0);
+ }
+ return StrokeData;
+}
+
// NOTE(fox): We only have to care about winding if we want to do HW accelerated
// shape subtraction with the stencil buffer (I think).
+// All the extra inputs on the second line are for when we need to take
+// interactive mode into account. Since undoing a transform requires knowledge
+// of the shape's size (does it?), the code needs to be ran twice, controlled
+// with the Interact bool.
static uint32
-NVG_FlattenPath(memory *Memory, shape_layer *Shape, nvg_point *PointData, int *Width, int *Height)
+NVG_FlattenPath(memory *Memory, shape_layer *Shape, nvg_point *PointData,
+ project_state *State, layer_transforms T, int Width, int Height,
+ int CompWidth, int CompHeight, bool32 Interact, v2 *Min, v2 *Max)
{
- uint32 NumberOfVerts = 0;
nvg_point *PointPlayhead = PointData;
for (int i = 0; i < Shape->Point_Count; i++) {
bezier_point *Point = Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, i, 1);
-#if 0
if (i == 0 || Point->Type == interpolation_type_linear) {
- *(v2 *)PointPlayhead = Point->Pos[0];
- if (i != 0 && i != (Shape->Point_Count - 1)) {
+ v2 Pos = Point->Pos[0];
+ if (State->Interact_Active == interact_type_keyframe_move && Point->IsSelected && Interact != 0) {
+ Pos = TransformPoint(T, Width, Height, Pos);
+ Pos.x += State->Interact_Offset[0];
+ Pos.y += State->Interact_Offset[1];
+ Pos = T_CompPosToLayerPos(T, CompWidth, CompHeight, Width, Height, Pos.x, Pos.y);
+ }
+ *(v2 *)PointPlayhead = Pos;
+ if (Shape->IsClosed || (i != 0 && i != (Shape->Point_Count - 1))) {
PointPlayhead->Flags |= NVG_PT_CORNER;
}
PointPlayhead++;
- NumberOfVerts++;
} else if (Point->Type == interpolation_type_bezier) {
bezier_point *Point_1 = Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, i-1, 1);
- v2 Pos[4] = { Point->Pos[0], Point->Pos[1], Point_1->Pos[2], Point_1->Pos[0] };
- Pos[1] = Pos[1] + Pos[0];
- Pos[2] = Pos[2] + Pos[3];
- NumberOfVerts += Bezier_CubicCalcPoints(Pos[3], Pos[2], Pos[1], Pos[0], PointPlayhead, sizeof(nvg_point));
+ v2 Pos[2] = { Point_1->Pos[0], Point->Pos[0] };
+ if (State->Interact_Active == interact_type_keyframe_move && Point->IsSelected && Width != 0) {
+ for (int i = 0; i < 2; i++) {
+ Pos[i] = TransformPoint(T, Width, Height, Pos[i]);
+ Pos[i].x += State->Interact_Offset[0];
+ Pos[i].y += State->Interact_Offset[1];
+ Pos[i] = T_CompPosToLayerPos(T, CompWidth, CompHeight, Width, Height, Pos[i].x, Pos[i].y);
+ }
+ }
+ PointPlayhead = (nvg_point *)Bezier_CubicCalcPoints(Pos[0], Pos[0] + Point_1->Pos[1], Pos[1] + Point->Pos[2], Pos[1], PointPlayhead, sizeof(nvg_point));
// The point at the end is also returned, so we remove it.
- NumberOfVerts--;
- PointPlayhead--;
+ if (i != (Shape->Point_Count - 1))
+ PointPlayhead--;
} else {
Assert(0);
}
-#else
- *(v2 *)PointPlayhead = Point->Pos[0];
- if (i != 0 && i != (Shape->Point_Count - 1)) {
- PointPlayhead->Flags |= NVG_PT_CORNER;
- }
- PointPlayhead++;
- NumberOfVerts++;
-#endif
}
+ int NumberOfVerts = PointPlayhead - PointData;
nvg_point *Point = &PointData[NumberOfVerts - 1];
nvg_point *NextPoint = PointData;
- v2 Min = V2(10000, 10000);
- v2 Max = V2(-10000, -10000);
+ *Min = V2(10000, 10000);
+ *Max = V2(-10000, -10000);
for (int i = 0; i < NumberOfVerts; i++) {
Point->dx = NextPoint->x - Point->x;
Point->dy = NextPoint->y - Point->y;
Point->Length = NVG_Normalize(&Point->dx, &Point->dy);
- if (Point->x > Max.x)
- Max.x = Point->x;
- if (Point->x < Min.x)
- Min.x = Point->x;
- if (Point->y > Max.y)
- Max.y = Point->y;
- if (Point->y < Min.y)
- Min.y = Point->y;
+ if (Point->x > Max->x)
+ Max->x = Point->x;
+ if (Point->x < Min->x)
+ Min->x = Point->x;
+ if (Point->y > Max->y)
+ Max->y = Point->y;
+ if (Point->y < Min->y)
+ Min->y = Point->y;
Point = NextPoint++;
}
- *Width = Max.x - Min.x;
- *Height = Max.y - Min.y;
return NumberOfVerts;
}
real32 MiterLimit = 2.4f;
static uint32
-NVG_ExpandStroke(void *Memory, int NumberOfVerts, real32 StartWidth, bool32 IsClosed, nvg_point *PointData, real32 *StrokeData)
+NVG_ExpandStroke(void *Memory, int NumberOfVerts, real32 StartWidth, nvg_line_cap LineCap, nvg_line_cap LineJoin, bool32 IsClosed, nvg_point *PointData, real32 *StrokeData)
{
real32 Width = StartWidth * 0.5;
int ncap = 12;
@@ -205,7 +257,11 @@ NVG_ExpandStroke(void *Memory, int NumberOfVerts, real32 StartWidth, bool32 IsCl
NextPoint = &PointData[1];
Start = 1;
LoopAmount = NumberOfVerts - 1;
- StrokeData = NVG_RoundCap(Point, StrokeData, Point->dx, Point->dy, Width, ncap, 0.5, 0.5, 0);
+ if (LineCap == NVG_ROUND) {
+ StrokeData = NVG_RoundCap(Point, StrokeData, Point->dx, Point->dy, Width, ncap, 0.5, 0.5, 0);
+ } else {
+ StrokeData = NVG_ButtCap(Point, StrokeData, Point->dx, Point->dy, Width, Width-1, 0.5, 0.5, 0);
+ }
}
for (int i = Start; i < LoopAmount; i++) {
@@ -229,6 +285,9 @@ NVG_ExpandStroke(void *Memory, int NumberOfVerts, real32 StartWidth, bool32 IsCl
NextPoint->dmy *= scale;
}
+ // Clear flags, but keep the corner.
+ NextPoint->Flags = (NextPoint->Flags & NVG_PT_CORNER) ? NVG_PT_CORNER : 0;
+
// Keep track of left turns.
cross = NextPoint->dx * Point->dy - Point->dx * NextPoint->dy;
if (cross > 0.0f) {
@@ -236,21 +295,22 @@ NVG_ExpandStroke(void *Memory, int NumberOfVerts, real32 StartWidth, bool32 IsCl
}
// Calculate if we should use bevel or miter for inner join.
- // limit = nvg__maxf(1.01f, nvg__minf(p0->len, p1->len) * iw);
- // if ((dmr2 * limit*limit) < 1.0f)
- // p1->flags |= NVG_PR_INNERBEVEL;
+ float iw = (Width > 0.0f) ? 1.0f / Width : 0.0f;
+ limit = Max(1.01f, Min(Point->Length, NextPoint->Length) * iw);
+ if ((dmr2 * limit*limit) < 1.0f)
+ NextPoint->Flags |= NVG_PR_INNERBEVEL;
// Check to see if the corner needs to be beveled.
if (NextPoint->Flags & NVG_PT_CORNER) {
- // if ((dmr2 * MiterLimit*MiterLimit) < 1.0f) r // || lineJoin == NVG_BEVEL || lineJoin == NVG_ROUND) {
- // NextPoint->Flags |= NVG_PT_BEVEL;
- // }
+ if (((dmr2 * MiterLimit*MiterLimit) < 1.0f) || LineJoin == NVG_BEVEL || LineJoin == NVG_ROUND) {
+ NextPoint->Flags |= NVG_PT_BEVEL;
+ }
}
if ((NextPoint->Flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
- // if (lineJoin == NVG_ROUND) {
+ if (LineJoin == NVG_ROUND) {
StrokeData = NVG_RoundJoin(Point, NextPoint, StrokeData, Width, Width, 0.5, 0.5, ncap);
- // }
+ }
} else {
StrokeData = NVG_Point(StrokeData, NextPoint->x + (NextPoint->dmx * Width), NextPoint->y + (NextPoint->dmy * Width), 0, 0);
StrokeData = NVG_Point(StrokeData, NextPoint->x - (NextPoint->dmx * Width), NextPoint->y - (NextPoint->dmy * Width), 0, 0);
@@ -260,7 +320,11 @@ NVG_ExpandStroke(void *Memory, int NumberOfVerts, real32 StartWidth, bool32 IsCl
}
if (!IsClosed) {
- StrokeData = NVG_RoundCap(NextPoint, StrokeData, Point->dx, Point->dy, Width, ncap, 0.5, 0.5, 1);
+ if (LineCap == NVG_ROUND) {
+ StrokeData = NVG_RoundCap(NextPoint, StrokeData, Point->dx, Point->dy, Width, ncap, 0.5, 0.5, 1);
+ } else {
+ StrokeData = NVG_ButtCap(NextPoint, StrokeData, Point->dx, Point->dy, Width, Width-1, 0.5, 0.5, 1);
+ }
} else {
StrokeData = NVG_Point(StrokeData, StartingStrokeData[0], StartingStrokeData[1], 0, 0);
StrokeData = NVG_Point(StrokeData, StartingStrokeData[4], StartingStrokeData[5], 0, 0);