Android下的匀速贝塞尔

画世界pro里的画笔功能很炫酷

其画笔配置可以调节流量,密度,色相,饱和度,亮度等。

他的大部分画笔应该是通过一个笔头图片在触摸轨迹上匀速绘制的原理。

这里提供一个匀速贝塞尔的kotlin实现:

class EvenBezier {private var lastControlPoint: ControllerPoint? = nullprivate var filterWeight = 0.5private var filterWeightInverse = 1 - filterWeight// 偏移量var stepOffset = 0.0// 间距var stepInterval = 5.0private val cValue = 0.33private val cValue2 = 0.1666private val cValue3 = 0.66private val curRawStroke = arrayListOf<ControllerPoint>()private val curRawSampledStroke = arrayListOf<ControllerPoint>()private val curFilteredStroke = arrayListOf<ControllerPoint>()fun begin(point: ControllerPoint): List<ControllerPoint> {curRawStroke.clear()curRawSampledStroke.clear()curFilteredStroke.clear()lastControlPoint = pointstepOffset = stepIntervalreturn extStroke(point)}fun extStroke(point: ControllerPoint): List<ControllerPoint> {val stepPoints = arrayListOf<ControllerPoint>()curRawStroke.add(point)curRawSampledStroke.add(point)val size = curRawStroke.sizeif (size >= 3) {val fPoint = calFilteredPoint(curRawSampledStroke[size-3],curRawSampledStroke[size-2],curRawSampledStroke[size-1],)curFilteredStroke.add(fPoint)}val filteredSize = curFilteredStroke.sizeif (filteredSize >= 3) {val list = createBezier(curFilteredStroke[filteredSize - 3],curFilteredStroke[filteredSize - 2],curFilteredStroke[filteredSize - 1],)stepPoints.addAll(list)}return stepPoints}private fun calFilteredPoint(p1: ControllerPoint, p2: ControllerPoint, p3: ControllerPoint): ControllerPoint {val m = p1.getMidPoint(p3)return ControllerPoint((filterWeight * p2.x + filterWeightInverse * m.x).toFloat(),(filterWeight * p2.y + filterWeightInverse * m.y).toFloat(),(filterWeight * p2.p + filterWeightInverse * m.p).toFloat(),)}fun endStroke(point: ControllerPoint): List<ControllerPoint> {LogUtils.d("endStroke--->")val stepPoints = arrayListOf<ControllerPoint>()curRawStroke.add(point)curRawSampledStroke.add(point)val size = curRawSampledStroke.sizeif (size >= 3) {val fPoint = calFilteredPoint(curRawSampledStroke[size-3],curRawSampledStroke[size-2],curRawSampledStroke[size-1],)curFilteredStroke.add(fPoint)} else {LogUtils.d("sample size: $size")}val filteredSize = curFilteredStroke.sizeif (filteredSize >=3) {val list = createBezier(curFilteredStroke[filteredSize - 3],curFilteredStroke[filteredSize - 2],curFilteredStroke[filteredSize - 1],)stepPoints.addAll(list)} else {LogUtils.d("sample filteredSize: $filteredSize")}curRawStroke.add(point)curRawSampledStroke.add(point)val size1 = curRawSampledStroke.sizeif (size1 >= 3) {val fPoint = calFilteredPoint(curRawSampledStroke[size1-3],curRawSampledStroke[size1-2],curRawSampledStroke[size1-1],)curFilteredStroke.add(fPoint)} else {LogUtils.d("sample size1: $size1")}val filteredSize1 = curFilteredStroke.sizeif (filteredSize1 >=3) {val list = createBezier(curFilteredStroke[filteredSize1 - 3],curFilteredStroke[filteredSize1 - 2],curFilteredStroke[filteredSize1 - 1],)stepPoints.addAll(list)} else {LogUtils.d("sample filteredSize1: $filteredSize1")}return stepPoints}private fun createBezier(pt0: ControllerPoint, pt1: ControllerPoint,pt2: ControllerPoint? = null): List<ControllerPoint> {val p0 = pt0val p3 = pt1val p0_x = p0.xval p0_y = p0.yval p0_p = p0.pval p3_x = p3.xval p3_y = p3.yval p3_p = p3.pval p1: ControllerPointif (lastControlPoint == null) {p1 = ControllerPoint((p0_x + (p3_x - p0_x)*cValue).toFloat(),(p0_y + (p3_y - p0_y)*cValue).toFloat(),(p0_p + (p3_p - p0_p)*cValue).toFloat(),)} else {p1 = lastControlPoint!!.getMirroredPoint(p0)}var p2: ControllerPointif (pt2 != null) {p2 = ControllerPoint((p3_x - (((p3_x - p0_x) + (pt2.x - p3_x)) * cValue2)).toFloat(),(p3_y - (((p3_y - p0_y) + (pt2.y - p3_y)) * cValue2)).toFloat(),(p3_p - (((p3_p - p0_p) + (pt2.p - p3_p)) * cValue2)).toFloat())} else {p2 = ControllerPoint((p0_x + (p3_x - p0_x) * cValue3).toFloat(),(p0_y + (p3_y - p0_y) * cValue3).toFloat(),(p0_p + (p3_p - p0_p) * cValue3).toFloat())}lastControlPoint = p2return calStepPoints(p0, p1, p2, p3)}private fun calStepPoints(p0: ControllerPoint, p1: ControllerPoint, p2: ControllerPoint,p3: ControllerPoint): List<ControllerPoint> {val stepPoints = arrayListOf<ControllerPoint>()var i = stepInterval// Value accessvar p0_x = p0.xvar p0_y = p0.yvar p0_p = p0.p// Algebraic conveniences, not geometricvar A_x = p3.x - 3 * p2.x + 3 * p1.x - p0_xvar A_y = p3.y - 3 * p2.y + 3 * p1.y - p0_yvar A_p = p3.p - 3 * p2.p + 3 * p1.p - p0_pvar B_x = 3 * p2.x - 6 * p1.x + 3 * p0_xvar B_y = 3 * p2.y - 6 * p1.y + 3 * p0_yvar B_p = 3 * p2.p - 6 * p1.p + 3 * p0_pvar C_x = 3 * p1.x - 3 * p0_xvar C_y = 3 * p1.y - 3 * p0_yvar C_p = 3 * p1.p - 3 * p0_pvar t = (i - stepOffset) / sqrt((C_x * C_x + C_y * C_y).toDouble())while (t <= 1.0) {// Pointvar step_x = t * (t * (t * A_x + B_x) + C_x) + p0_xvar step_y = t * (t * (t * A_y + B_y) + C_y) + p0_yvar step_p = t * (t * (t * A_p + B_p) + C_p) + p0_pstepPoints.add(ControllerPoint(step_x.toFloat(),step_y.toFloat(),step_p.toFloat()));// Step distance until next onevar s_x = t * (t * 3 * A_x + 2 * B_x) + C_x // dx/dtvar s_y = t * (t * 3 * A_y + 2 * B_y) + C_y // dy/dtvar s = sqrt(s_x * s_x + s_y * s_y) // s = derivative in 2D spacevar dt = i / s // i = interval / derivative in 2Dt += dt}if (stepPoints.size == 0) // We didn't step at all along this BezierstepOffset += p0.getDistance(p3)elsestepOffset = stepPoints.last().getDistance(p3)return stepPoints}
}

在画笔的onTouch里进行相应的调用即可。

    private fun touchDown(e: MotionEvent) {mBezier.stepInterval = getStepSpace()mPointList.clear()val list = mBezier.begin(ControllerPoint(e.x, e.y, getPressValue(e)))mHWPointList.addAll(list)}private fun touchMove(e: MotionEvent) {val list = mBezier.extStroke(ControllerPoint(e.x, e.y, getPressValue(e)))mPointList.addAll(list)}private fun touchUp(e: MotionEvent) {val list = mBezier.endStroke(ControllerPoint(e.x, e.y, getPressValue(e)))mPointList.addAll(list)}

mPointList就是个匀速贝塞尔的点集合。getPressValue是获取当前点的按压力度,以更好实现笔的特效。

最终的匀速效果:

有瑕疵,点数太少时并不是匀速的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/278629.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端安全——最新:lodash原型漏洞从发现到修复全过程

人生的精彩就在于你永远不知道惊喜和意外谁先来&#xff0c;又是一个平平无奇的早晨&#xff0c;我收到了一份意外的惊喜——前端某项目出现lodash依赖原型污染漏洞。咋一听&#xff0c;很新奇。再仔细一看&#xff0c;呕吼&#xff0c;更加好奇了~然后就是了解和修补漏洞之旅。…

PHP反序列化--引用

一、引用的理解&#xff1a; 引用就是给予一个变量一个恒定的别名。 int a 10; int b &a; a 20; cout<<a<<b<<endl; 输出结果 : a20、b20 二、靶场复现&#xff1a; <?php highlight_file(__FILE__); error_reporting(0); include("flag.p…

留学文书可以彻底被AI取代吗?升学指导这一职业是否会被AI逼到墙角?

近日&#xff0c;ChatGPT再次“进化”&#xff0c;其最新版本ChatGPT-4又掀高潮。其生产者OpenAI 称&#xff0c;“ChatGPT-4是最先进的系统&#xff0c;能生产更安全和更有用的回复。”和上一代相比&#xff0c;GPT-4拥有了更广的知识面和更强的解决问题能力&#xff0c;在创意…

VSCode + PicGo + Github 实现markdown图床管理

目录 PicGo客户端VSvode插件 PicGo客户端 PicGo 是一个图片上传管理工具 官网&#xff1a;https://molunerfinn.com/PicGo/ github图传使用说明&#xff1a;https://picgo.github.io/PicGo-Doc/zh/guide/config.html#GitHub图床 步骤&#xff1a; 1、创建一个github公开仓库…

Java解决完全二叉树的节点个数

Java解决完全二叉树的节点个数 01 题目 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的…

Jenkins实现CICD(3)_Jenkins连接到git

文章目录 1、如何完成上述操作&#xff0c;并且不报如下错&#xff1a;2、连接不上git&#xff0c;操作如下&#xff1a;3、将上边产生的3个文件拷贝到&#xff1a;C:\Windows\System32\config\systemprofile\.ssh4、新建下图凭证&#xff1a;创建步骤&#xff1a; 5、公钥填到…

【数据结构与算法】:非递归实现快速排序、归并排序

&#x1f525;个人主页&#xff1a; Quitecoder &#x1f525;专栏&#xff1a;数据结构与算法 上篇文章我们详细讲解了递归版本的快速排序&#xff0c;本篇我们来探究非递归实现快速排序和归并排序 目录 1.非递归实现快速排序1.1 提取单趟排序1.2 用栈实现的具体思路1.3 代码…

QY-02-W2 无线雨量监测站 防洪防汛 大坝水库雨水情实时监测

无线雨量监测站配置&#xff1a; 不锈钢支架、雨量传感器、太阳能板、数据采集发送器。 产品概述 无线雨量监测站是一款无线传输数据的雨量监测设备&#xff0c;由不锈钢支架、ABS雨量筒、不锈钢防护箱、主机采集器、无线数据发送装置、太阳能电池板、蓄电池等构成&#xff0c…

体验OceanBase OBD V2.5.0 组件内扩容和组件变更

背景 OBD 是OceanBase的命令行部署工具&#xff0c;在 obd V2.5.0 版本之前&#xff0c;其主要功能主要是部署各类组件&#xff0c;例如 oceanbase-ce,obproxy-ce,obagent 等。然而&#xff0c;它并不支持组件的变更操作以及组件内部的扩缩容调整。具体来说&#xff1a; 1、若…

利用自定义 URI Scheme 在 Android 应用中实现安全加密解密功能

在现代移动应用开发中&#xff0c;安全性和用户体验是至关重要的考虑因素。在 Android 平台上&#xff0c;开发人员可以利用自定义 URI Scheme 和 JavaScript 加密解密技术来实现更安全的数据传输和处理。本文将介绍如何在 Android 应用中注册自定义 URI Scheme&#xff0c;并结…

鸿蒙App开发学习 - TypeScript编程语言全面开发教程(下)

现在我们接着上次的内容来学习TypeScript编程语言全面开发教程&#xff08;下半部分&#xff09; 4. 泛型 TypeScript 中的泛型&#xff08;Generics&#xff09;是一种编程模式&#xff0c;用于在编写代码时增强灵活性和可重用性。泛型使得在定义函数、类、接口等数据类型时…

Machine Learning ---- Feature Scaling

目录 一、What is feature scaling:&#xff1a; 二、Why do we need to perform feature scaling? 三、How to perform feature scaling: 1、Normalization: 2、Mean normalization: 3、Standardization (data needs to follow a normal distribution): 一、What is featur…

关于 闰年 的小知识,为什么这样判断闰年

闰年的规定&#xff1a; 知道了由来&#xff0c;我们就可以写程序来判断&#xff1a; #include <stdio.h> int main() {int year, leap;scanf("%d",&year);if((year%4 0 && year%100 ! 0) || year%400 0)leap 1;else leap 0;if(leap) printf(…

26-Java访问者模式 ( Visitor Pattern )

Java访问者模式 摘要实现范例 访问者模式&#xff08;Visitor Pattern&#xff09;使用了一个访问者类&#xff0c;它改变了元素类的执行算法&#xff0c;通过这种方式&#xff0c;元素的执行算法可以随着访问者改变而改变访问者模式中&#xff0c;元素对象已接受访问者对象&a…

为什么说CRM是制造业数字化模型的源头?

2023年&#xff0c;中国制造业投资持续企稳向好&#xff0c;全年增长6.5%。制造业发展韧性强劲&#xff0c;但如何激发有效需求&#xff0c;在防范产能过剩的同时实现产业升级&#xff0c;这是摆在当下诸多制造企业面前的现实问题。 山东章鼓企划信息管理处部长、山东鲸头鹳智…

win修改图标自定义QQ桌面图标

当安装了TIM后&#xff0c;想把图标改成QQ 图标见顶部&#xff0c;或通过网盘下载 提取码&#xff1a;9Ayc 操作步骤&#xff1a; 1.桌面右键图标&#xff0c;点击属性 2.选择快捷方式-更改图标 3.浏览选择下载的ico图标即可

基于单片机的老人防丢系统设计

目 录 摘 要 I Abstract II 引 言 3 1 系统总体架构 6 1.1方案设计与选择 6 1.2 系统架构设计 6 1.3 系统器件选择 7 2 系统硬件设计 9 2.1 单片机外围电路设计 9 2.2 LCD1602液晶显示电路设计 12 2.3 短信模块电路设计 14 2.4 GPS模块电路设计 14 2.5 电源与按键控制电路设计…

C++——字符串、读写文件、结构体、枚举

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的夜间车辆检测系统(深度学习代码+UI界面+训练数据集)

摘要&#xff1a;开发夜间车辆检测系统对于自动驾驶技术具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个夜间车辆检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#xff0c;展示了不同模型间…

力扣坑题:回文排列

bool canPermutePalindrome(char* s){ int arr[128]{0}; for(int x0;x<strlen(s);x) { arr[(s[x])]; } int flag0; for(int i0;i<128;i) {if(arr[i]%2!0){flag;} } if(flag>1) {return false; } return true; }没想到吧&#xff0c;不止26个英文字母