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.mzph.cn/news/754541.shtml

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

相关文章

Spring Data访问Elasticsearch----其他Elasticsearch操作支持

Spring Data访问Elasticsearch----其他Elasticsearch操作支持 一、索引设置二、索引映射三、Filter Builder 一、索引设置 二、索引映射 三、Filter Builder

Tensorflow2.0 - 链式法则例子

本笔记简单记录链式法则的原理&#xff0c;关于链式法则&#xff0c;本身和高等数学中的链式求导法则是一样的&#xff0c;深度学习中相关资料可以参考这里&#xff1a; 【深度学习之美22】BP算法详解之链式法则 - 知乎10.5 什么是计算图&#xff1f;我们知道&#xff0c; 神经…

docker服务起不来原因及解决

目录 问题原因排查查看docker服务的状态尝试重启docker service查看 log分析原因 解决方案参考解决过程 报错 关键词&#xff1a; Failed to start Docker Application Container Engine. Failed to find iptables: exec: \"iptables\": executable file not found i…

uniapp微信小程序随机生成canvas-id报错?

uniapp微信小程序随机生成canvas-id报错&#xff1f; 文章目录 uniapp微信小程序随机生成canvas-id报错&#xff1f;效果图遇到问题解决 场景&#xff1a; 子组件&#xff0c;在 mounted 绘制 canvas&#xff1b;App、H5端正常显示&#xff0c;微信小程序报错&#xff1b; 效…

华为设备配置命令大全

目录 一、华为设备常用命令视图 二、返回命令和保存命令 三、设置设备名称 四、关闭泛洪信息 五、设置设备接口的IP地址和子网掩码 六、交换机的登录 6.1、设置Consile接口密码 6.2、设置Telent接口密码 七、VLAN配置 7.1、创建VLAN 7.2、进入vlan视图 7.3、把端口…

开源自动GPT和BabyAGI将递归整合到AI应用中

开源Auto-GPT与BabyAGI&#xff1a;将递归融入AI应用 近期&#xff0c;Auto-GPT和BabyAGI的发展展示了自主智能体的巨大潜力&#xff0c;引起了AI研究和软件开发领域的极大关注。这些基于大型语言模型&#xff08;LLM&#xff09;的智能体能够响应用户提示&#xff0c;执行复杂…

内网穿透的应用-如何使用Docker安装DockerUI可视化管理工具无公网IP远程访问

文章目录 前言1. 安装部署DockerUI2. 安装cpolar内网穿透3. 配置DockerUI公网访问地址4. 公网远程访问DockerUI5. 固定DockerUI公网地址 前言 DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基…

Java 文件序列化和反序列化

list序列化 /*** 序列化* param list* param filename* throws IOException*/public static void serializeList(List<Map<String, Object>> list, String filename) throws IOException {try (ObjectOutputStream oos new ObjectOutputStream(new FileOutputStre…

WPF意外无法启动?try-catch也无法捕捉?0xc0000409?

文章目录 背景尝试原因解决 背景 周六在家加了一会会的班&#xff0c;公司电脑没关机&#xff0c;然后周一上班。。。诡异的事情发生了&#xff0c;在家远程都能运行的程序&#xff0c;突然运行不起来了 尝试 我对WPF程序做了如下尝试&#xff1a; 修改UI框架对OnStartup方…

MatherCup一等奖——基于时间序列、LSTM等预测优化类1314模型集的论文分享

该构建的模型适用大多数电商零售商家的预测问题&#xff0c;本文决定综合多种 时间序列预测 方法创 建一个模型集&#xff0c;将之作为一个新的预测模型来解决实际预测问题。 本文首先对数据预处理&#xff0c;将附件 1-6 的数据合并&#xff0c;将非数值数据转换为可识别的数…

Android-Framework pm list packages和pm install返回指定应用信息

一、环境 高通 Android 13 注&#xff1a;Android10 和Android13有些差异&#xff0c;代码位置不变&#xff0c;参照修改即可 二、pm简单介绍 pm工具为包管理&#xff08;package manager&#xff09;的简称 可以使用pm工具来执行应用的安装和查询应用宝的信息、系统权限、…

华为配置终端定位基本实验配置

配置终端定位基本示例 组网图形 图1 配置终端定位基本服务示例 组网需求数据准备配置思路配置注意事项操作步骤配置文件 组网需求 如图1所示&#xff0c;某公司网络中&#xff0c;中心AP直接与RU连接。 管理员希望通过RU收集Wi-Fi终端信息&#xff0c;并提供给定位服务器进行定…

Flutter-仿淘宝京东录音识别图标效果

效果 需求 弹起键盘&#xff0c;录制按钮紧挨着输入框收起键盘&#xff0c;录制按钮回到初始位置 实现 第一步&#xff1a;监听键盘弹起并获取键盘高度第二步&#xff1a;根据键盘高度&#xff0c;录制按钮高度计算偏移高度&#xff0c;并动画移动第三步&#xff1a;键盘收起…

C语言 02 安装

C 语言的编译器有很多&#xff0c;其中最常用的是 GCC&#xff0c;这里以安装 GCC 为例。 Windows 这里以 Windows 11 为例 官方下载地址&#xff1a;https://www.mingw-w64.org/ 选择 Downloads 选择 Windows 的 GCC 环境 MingW-W64-builds 选择 GitHub 根据操作系统位…

Qt5.14.2 深入理解Qt多线程编程,掌握线程池架构实现高效并发

在高并发的软件系统中&#xff0c;多线程编程是解决性能瓶颈和提高系统吞吐量的有效手段。作为跨平台的应用程序开发框架&#xff0c;Qt为我们提供了强大的多线程支持。本文将深入探讨Qt多线程编程的实现细节&#xff0c;并介绍线程池的设计思想&#xff0c;帮助读者彻底掌握Qt…

网盘聚合工具:统筹管理所有网盘资源 | 开源日报 No.203

alist-org/alist Stars: 35.6k License: AGPL-3.0 alist 是一个支持多存储的文件列表/WebDAV 程序&#xff0c;使用 Gin 和 Solidjs。 该项目的主要功能、关键特性、核心优势包括&#xff1a; 支持多种存储方式易于部署和开箱即用文件预览&#xff08;PDF、markdown、代码等&…

SpringBoot2.7集成Swagger3

Swagger2已经在17年停止维护了&#xff0c;取而代之的是 Swagger3&#xff08;基于openApi3&#xff09;&#xff0c;所以新项目要尽量使用Swagger3. Open API OpenApi是业界真正的 api 文档标准&#xff0c;其是由 Swagger 来维护的&#xff0c;并被linux列为api标准&#x…

Web基础06-AJAX,Axios,JSON数据

目录 一、AJAX 1.概述 2.主要作用 3.快速入门 4.AJAX的优缺点 &#xff08;1&#xff09;优点 &#xff08;2&#xff09;缺点 5.同源策略 二、Axios 1.概述 2.快速入门 3.请求方式别名 三、JSON 1.概述 2.主要作用 3.基础语法 4.JSON数据转换 &#xff08;1…

Windows11安装Msql8.0版本详细安装步骤!

文章目录 前言一、下载Mysql二、安装Mysql三、登录验证三、环境变量配置总结 前言 每次搭建新环境的时候&#xff0c;都需要网上搜寻安装的步骤教程&#xff01;为了以后方便查阅&#xff01;那么本次就记录一下Windows11安装Msql8.0的详细步骤&#xff01;也希望能帮助到有需…

npm和pnpm安装、更换镜像源

安装pnpm 1 wins 在系统中搜索框 输入“Windos PowerShell”右击“管理员身份运行” 2 输入“set-ExecutionPolicy RemoteSigned”回车,根据提示输入A&#xff0c;回车 3 输入 pnpm -v 查看版本 如果没有版本好就是没有安装 pnpm 输入安装命令 npm install -g pnpm 4 再次 …