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,一经查实,立即删除!

相关文章

Tensorflow2.0 - 链式法则例子

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

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

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

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

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

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

配置终端定位基本示例 组网图形 图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;也希望能帮助到有需…

抖音无水印视频关键词批量下载|视频下载工具

抖音无水印视频关键词批量下载操作说明 我们根据自己的需要开发了抖音视频批量下载工具&#xff0c;现在市面上的视频无水印工具只能通过单个视频链接进行提取&#xff0c;太不方便 所以我们延伸出了 不仅可以通过单个视频链接进行提取也可通过关键词进行视频搜索 进行批量和有…

Python基于深度学习的中文情感分析系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

计算机设计大赛 题目:基于深度学习的中文汉字识别 - 深度学习 卷积神经网络 机器视觉 OCR

文章目录 0 简介1 数据集合2 网络构建3 模型训练4 模型性能评估5 文字预测6 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的中文汉字识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &a…

【AAAI 2024】MuLTI:高效视频与语言理解

一、背景 1.1 多模态的发展 多模态理解模型具有广泛的应用&#xff0c;比如多标签分类&#xff08;Classification&#xff09;、视频问答&#xff08;videoQA&#xff09;和文本视频检索&#xff08;Retrieval&#xff09;等。现有的方法已经在视频和语言理解方面取得了重大…

unity发布安卓获取读取权限

一、Player Settings 设置 Player Settings>Player>Other Settings> Android > Write Permission > External (SDCard). 二、代码 using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Andr…

【c++】内联-引用-重载

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;c_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.【c】内联函数 1.1 背景 1.2 内联函数的概念 1.3 内联函数的特性 1.4 宏和内联的小知识 宏的优缺点&#xff1f; C有哪些技术替代…

mac npm install 很慢或报错

npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.org/pnpm failed, reason: certificate has expired 1、取消ssl验证&#xff1a; npm config set strict-ssl false 修改后一般就可以了&#xff0c;…

kingbase 服务器配置(参数修改)

引言&#xff1a; 人大金仓作为国产数据库的佼佼者(单机)&#xff0c;也是每位数据库从业者必须数据库之一 配置文件 kingbase 参数配置 主要由 kingbase.conf 和 kingbase.auto.conf 设置 kingbase.conf 该参数文件为主配置文件&#xff0c;一般情况下&#xff0c;需要 重启…

day03vue学习

day03 一、今日目标 1.生命周期 生命周期介绍生命周期的四个阶段生命周期钩子声明周期案例 2.综合案例-小黑记账清单 列表渲染添加/删除饼图渲染 3.工程化开发入门 工程化开发和脚手架项目运行流程组件化组件注册 4.综合案例-小兔仙首页 拆分模块-局部注册结构样式完善…