#include "include/core/SkPath.h"
#include "src/core/SkGeometry.h"
#include "src/core/SkPointPriv.h"
#include "src/core/SkStrokerPriv.h"
#include <utility>
namespace pk {
static void ButtCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
const SkPoint& stop, SkPath*) {
path->lineTo(stop.fX, stop.fY);
}
static void RoundCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
const SkPoint& stop, SkPath*) {
SkVector parallel;
SkPointPriv::RotateCW(normal, ¶llel);
SkPoint projectedCenter = pivot + parallel;
path->conicTo(projectedCenter + normal, projectedCenter, PK_ScalarRoot2Over2);
path->conicTo(projectedCenter - normal, stop, PK_ScalarRoot2Over2);
}
static void SquareCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
const SkPoint& stop, SkPath* otherPath) {
SkVector parallel;
SkPointPriv::RotateCW(normal, ¶llel);
if (otherPath) {
path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
} else {
path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
path->lineTo(stop.fX, stop.fY);
}
}
static bool is_clockwise(const SkVector& before, const SkVector& after) {
return before.fX * after.fY > before.fY * after.fX;
}
enum AngleType {
kNearly180_AngleType,
kSharp_AngleType,
kShallow_AngleType,
kNearlyLine_AngleType
};
static AngleType Dot2AngleType(SkScalar dot) {
if (dot >= 0) { return SkScalarNearlyZero(PK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
} else { return SkScalarNearlyZero(PK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
}
}
static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) {
#if 1
inner->lineTo(pivot.fX, pivot.fY);
#endif
inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
}
static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
const SkPoint& pivot, const SkVector& afterUnitNormal,
SkScalar radius, SkScalar invMiterLimit, bool, bool) {
SkVector after;
afterUnitNormal.scale(radius, &after);
if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) {
using std::swap;
swap(outer, inner);
after.negate();
}
outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
HandleInnerJoin(inner, pivot, after);
}
static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
const SkPoint& pivot, const SkVector& afterUnitNormal,
SkScalar radius, SkScalar invMiterLimit, bool, bool) {
SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
AngleType angleType = Dot2AngleType(dotProd);
if (angleType == kNearlyLine_AngleType)
return;
SkVector before = beforeUnitNormal;
SkVector after = afterUnitNormal;
SkRotationDirection dir = kCW_SkRotationDirection;
if (!is_clockwise(before, after)) {
using std::swap;
swap(outer, inner);
before.negate();
after.negate();
dir = kCCW_SkRotationDirection;
}
SkMatrix matrix;
matrix.setScale(radius, radius);
matrix.postTranslate(pivot.fX, pivot.fY);
SkConic conics[SkConic::kMaxConicsForArc];
int count = SkConic::BuildUnitArc(before, after, dir, &matrix, conics);
if (count > 0) {
for (int i = 0; i < count; ++i) {
outer->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
}
after.scale(radius);
HandleInnerJoin(inner, pivot, after);
}
}
#define kOneOverSqrt2 (0.707106781f)
static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
const SkPoint& pivot, const SkVector& afterUnitNormal,
SkScalar radius, SkScalar invMiterLimit,
bool prevIsLine, bool currIsLine) {
SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
AngleType angleType = Dot2AngleType(dotProd);
SkVector before = beforeUnitNormal;
SkVector after = afterUnitNormal;
SkVector mid;
SkScalar sinHalfAngle;
bool ccw;
if (angleType == kNearlyLine_AngleType) {
return;
}
if (angleType == kNearly180_AngleType) {
currIsLine = false;
goto DO_BLUNT;
}
ccw = !is_clockwise(before, after);
if (ccw) {
using std::swap;
swap(outer, inner);
before.negate();
after.negate();
}
if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) {
mid = (before + after) * radius;
goto DO_MITER;
}
sinHalfAngle = PkScalarSqrt(PkScalarHalf(PK_Scalar1 + dotProd));
if (sinHalfAngle < invMiterLimit) {
currIsLine = false;
goto DO_BLUNT;
}
if (angleType == kSharp_AngleType) {
mid.set(after.fY - before.fY, before.fX - after.fX);
if (ccw) {
mid.negate();
}
} else {
mid.set(before.fX + after.fX, before.fY + after.fY);
}
mid.setLength(radius / sinHalfAngle);
DO_MITER:
if (prevIsLine) {
outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
} else {
outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
}
DO_BLUNT:
after.scale(radius);
if (!currIsLine) {
outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
}
HandleInnerJoin(inner, pivot, after);
}
SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) {
const SkStrokerPriv::CapProc gCappers[] = {
ButtCapper, RoundCapper, SquareCapper
};
return gCappers[cap];
}
SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) {
const SkStrokerPriv::JoinProc gJoiners[] = {
MiterJoiner, RoundJoiner, BluntJoiner
};
return gJoiners[join];
}
}