Bezier n阶通用版本C++实现
#pragma once #include "gstl.h" #include <vector> #include "SmoothSampling.h" using namespace gstl; /* n阶 bezier 曲线通用公式 */ class GachaBazier { public: GachaBazier(const Vector2<f32>& start,const Vector2<f32>& dest); void addCtrlPoint(const Vector2<f32>& ctrlPoint); Vector2<f32> getInterpolatePos(float t); private: float getKanbudong(float n, float i); float getJieCheng(int n); private: Vector2<f32> m_start; Vector2<f32> m_dest; std::vector<Vector2<f32>> m_ctrl; }; class GachaBezierSmooth : public BoloScriptFastLib,public GachaBazier { generatedBoloObject(GachaBezierSmooth,"GachaBezierSmooth"); GachaBezierSmooth(); virtual ~GachaBezierSmooth(); public: void sampling(int fSamplingNum = 100); float getSmoothY(float fx); vec2 getMinSampling(); vec2 getMaxSampling(); void setStart(vec2 tStart); void setEnd(vec2 tEnd); private: SmoothSampling m_tSmoothSampling; // 网页上 看到的 起始点和目标点都是 写死的 0,0 1,1 两个点, 对应返回的百分比需要按比例变大变小 vec2 m_tStart; vec2 m_tEnd; }; /* 3次贝塞尔曲线差值器 http://cubic-bezier.com css3 3次贝塞尔曲线 cubic-bezier https://blog.csdn.net/zhaozjc112/article/details/52909172/ 起点永远算作 (0,0) 终点永远算作 (1,1) 控制点有且只有2个 (m_x1,m_y1) (m_x2,m_y2) */ class GachaCubicBezierInter { public: GachaCubicBezierInter(); ~GachaCubicBezierInter(); void setPt1(float x,float y); void setPt2(float x,float y); void prepare(); f32 getY(float x); private: Vector2<f32> m_pt1; Vector2<f32> m_pt2; GachaBazier* m_bezier = nullptr; };
#include "GachaBezierInter.h" #include <math.h> #include <cassert> extern float clampf(float& v, float min, float max); GachaBazier::GachaBazier(const Vector2<f32>& start, const Vector2<f32>& dest) { m_start = start; m_dest = dest; m_ctrl.push_back(m_start); m_ctrl.push_back(m_dest); } void GachaBazier::addCtrlPoint(const Vector2<f32>& point) { Vector2<f32> pt = point; size_t len = m_ctrl.size(); m_ctrl.insert(m_ctrl.begin() + len - 1, pt); } Vector2<f32> GachaBazier::getInterpolatePos(float t) { //t = clampf(t, -1.f, 1.f); if (t < -1.f) { printf("dddaaa\n"); } if (t < 0.f) { printf("fsfdsa\n"); } if (t > 1.f) { printf("ddd\n"); } Vector2<f32> pt; assert(m_ctrl.size() >= 2); int n = m_ctrl.size() - 1; for (int i = 0; i <= n; i++) { pt.x += getKanbudong(n, i) * pow((1 - t), n - i) * pow(t, i) * m_ctrl[i].x; pt.y += getKanbudong(n, i) * pow((1 - t), n - i) * pow(t, i) * m_ctrl[i].y; } return pt; } float GachaBazier::getKanbudong(float n, float i) { return getJieCheng(n) / (getJieCheng(i) * getJieCheng(n - i)); } float GachaBazier::getJieCheng(int n) { float result = 1.0f; for (int i = 1; i <= n; i++) { result *= i; } return result; } ////////////////////////////////////////////////////////////////////////// GachaBezierSmooth::~GachaBezierSmooth() { } GachaBezierSmooth::GachaBezierSmooth() : GachaBazier(vec2::zero, vec2(1.f, 1.f)) { } void GachaBezierSmooth::setStart(vec2 tStart) { m_tStart = tStart; } void GachaBezierSmooth::setEnd(vec2 tEnd) { m_tEnd = tEnd; } void GachaBezierSmooth::sampling(int fSamplingNum) { // 需要添加对应的采样点 float fDelay = 1.f / fSamplingNum; for (float i = 0.f; i < 1.f; i += fDelay) { vec2 tVec2 = getInterpolatePos(i); tVec2 = (m_tEnd - m_tStart) * tVec2 + m_tStart; m_tSmoothSampling.insertPoint(tVec2.x, tVec2.y); } } float GachaBezierSmooth::getSmoothY(float fx) { return m_tSmoothSampling.getSmooth(fx); } vec2 GachaBezierSmooth::getMinSampling() { auto it = m_tSmoothSampling.m_sortMpSampling.begin(); if (it == m_tSmoothSampling.m_sortMpSampling.end()) { return vec2::zero; } return vec2(it->first, it->second); } vec2 GachaBezierSmooth::getMaxSampling() { if (m_tSmoothSampling.m_sortMpSampling.size() <= 0) { return vec2::zero; } auto it = m_tSmoothSampling.m_sortMpSampling.end(); it--; return vec2(it->first, it->second); } SC_Entry GachaBezierSmooth_setStart(BoloScriptFast* stack, BoloScriptFastLib* ptr) { GachaBezierSmooth* pGachaBezierSmooth = dynamic_cast<GachaBezierSmooth*>(ptr); float fx = bolo_float(stack); float fy = bolo_float(stack); pGachaBezierSmooth->setStart(vec2(fx, fy)); return SC_Entry(); } SC_Entry GachaBezierSmooth_setEnd(BoloScriptFast* stack, BoloScriptFastLib* ptr) { GachaBezierSmooth* pGachaBezierSmooth = dynamic_cast<GachaBezierSmooth*>(ptr); float fx = bolo_float(stack); float fy = bolo_float(stack); pGachaBezierSmooth->setEnd(vec2(fx, fy)); return SC_Entry(); } SC_Entry GachaBezierSmooth_addCtrlPoint(BoloScriptFast* stack, BoloScriptFastLib* ptr) { GachaBezierSmooth* pGachaBezierSmooth = dynamic_cast<GachaBezierSmooth*>(ptr); float fx = bolo_float(stack); float fy = bolo_float(stack); pGachaBezierSmooth->addCtrlPoint(vec2(fx, fy)); return SC_Entry(); } SC_Entry GachaBezierSmooth_sampling(BoloScriptFast* stack, BoloScriptFastLib* ptr) { GachaBezierSmooth* pGachaBezierSmooth = dynamic_cast<GachaBezierSmooth*>(ptr); int nSamplingNum = bolo_int(stack); pGachaBezierSmooth->sampling(nSamplingNum); return SC_Entry(); } SC_Entry GachaBezierSmooth_getSmoothY(BoloScriptFast* stack, BoloScriptFastLib* ptr) { GachaBezierSmooth* pGachaBezierSmooth = dynamic_cast<GachaBezierSmooth*>(ptr); float fx = bolo_float(stack); return pGachaBezierSmooth->getSmoothY(fx); } SC_Entry GachaBezierSmooth_getMinSampling(BoloScriptFast* stack, BoloScriptFastLib* ptr) { GachaBezierSmooth* pGachaBezierSmooth = dynamic_cast<GachaBezierSmooth*>(ptr); vec2 tPos = pGachaBezierSmooth->getMinSampling(); SC_Entry* pVec = new SC_Entry[2] { tPos.x, tPos.y }; return SC_Entry(pVec, 2); } SC_Entry GachaBezierSmooth_getMaxSampling(BoloScriptFast* stack, BoloScriptFastLib* ptr) { GachaBezierSmooth* pGachaBezierSmooth = dynamic_cast<GachaBezierSmooth*>(ptr); vec2 tPos = pGachaBezierSmooth->getMaxSampling(); SC_Entry* pVec = new SC_Entry[2] { tPos.x, tPos.y }; return SC_Entry(pVec, 2); } void GachaBezierSmooth::registerReflection(s32 id) { registerFunc(id, "setStart", GachaBezierSmooth_setStart); registerFunc(id, "setEnd", GachaBezierSmooth_setEnd); registerFunc(id, "addCtrlPoint", GachaBezierSmooth_addCtrlPoint); registerFunc(id, "sampling", GachaBezierSmooth_sampling); registerFunc(id, "getSmoothY", GachaBezierSmooth_getSmoothY); registerFunc(id, "getMinSampling", GachaBezierSmooth_getMinSampling); registerFunc(id, "getMaxSampling", GachaBezierSmooth_getMaxSampling); } /////////////////////////////// GachaCubicBezierInter::GachaCubicBezierInter() { m_pt1.x = 0.f; m_pt1.y = 0.f; m_pt2.x = 1.f; m_pt2.y = 1.f; Vector2<f32> start(0.f, 0.f); Vector2<f32> dest(1.f,1.f); m_bezier = new GachaBazier(start,dest); } GachaCubicBezierInter::~GachaCubicBezierInter() { delete m_bezier; m_bezier = nullptr; } f32 GachaCubicBezierInter::getY(float x) { Vector2<f32> pos = m_bezier->getInterpolatePos(x); return pos.y; } void GachaCubicBezierInter::setPt1(float x, float y) { m_pt1.x = x; m_pt1.y = y; } void GachaCubicBezierInter::setPt2(float x, float y) { m_pt2.x = x; m_pt2.y = y; } void GachaCubicBezierInter::prepare() { m_bezier->addCtrlPoint(m_pt1); m_bezier->addCtrlPoint(m_pt2); }