GL绘制自定义线条3_自定义线帽
安卓Path搭配Paint可以设置线帽,我想能不能把我自己的线条绘制Demo也加上类似的功能。
线头规则描述:
1、设一个线宽一半的线段,坐标为(0, 0)到(-lineWidth / 2, 0)。
2、设步骤1的线段有一垂直于它的向量(0,1),然后传入最近两次触摸坐标,并将第二次触摸坐标减去第一次触摸触摸坐标,得到当前画线的前进方向向量,然后求与(0, 1)向量夹角。
3、以从步骤2中得到的夹角,到该夹角+180度为止,以一定的角度步进量旋转步骤1的线段,形成一个切合线段的半圆。
4、最后添加一个(-lineWidth / 2, 0)到(lineWidth / 2, 0)的线段并旋转到步骤3的最终角度,方便和线段本体对接。
线尾规则描述:
1、只需把线头的规则中的步骤二改为,最后一次的之前一次的触摸坐标 减去 最后一次的触摸坐标作为方向向量即可(和前进方向相反)。
2、去冗余处理,每前进一次,就把之前添加的线尾半圆顶点删掉。
关键代码:
/**给线头添加符合线宽的边界,便于和纤体本身链接**/private void lineCapAddBorder(double angle, float firstVec[], List<float[]> newVecs) {try {float rotatedVec0[] = rotate2d(new float[] {-mLineWidth / 2f, 0}, angle + 180);float rotatedVec1[] = rotate2d(new float[] {mLineWidth / 2f, 0}, angle + 180);float newVec[] = new float[6];//偏移到对应位置newVec[0] = rotatedVec0[0] + firstVec[0];newVec[1] = rotatedVec0[1] + firstVec[1];newVec[3] = rotatedVec1[0] + firstVec[0];newVec[4] = rotatedVec1[1] + firstVec[1];newVecs.add(newVec);} catch (Exception e) {e.printStackTrace();}}/**绘制线头* @param isHead 是否曲线头部添加线帽,否则视为曲线尾部添加线帽**/private int lineCap(boolean isHead, @NonNull float firstVec[], @NonNull float secVec[], int color) {if (null == firstVec) {return -1;}if (mHeadPointBuf == null) {mHeadCapPointByteBuffer = ByteBuffer.allocateDirect(mHeadInitVertexCount * 4); //顶点数 * sizeof(float)mHeadCapPointByteBuffer.order(ByteOrder.nativeOrder());mHeadPointBuf = mHeadCapPointByteBuffer.asFloatBuffer();mHeadPointBuf.position(0);mHeadCapPointBufferPos = 0;}//按初始化大小初始化RGBA字节数组和RGBA数组if (mHeadColorBuf == null) {mHeadCapColorByteBuffer = ByteBuffer.allocateDirect(mHeadInitColorCount * 4);mHeadCapColorByteBuffer.order(ByteOrder.nativeOrder());mHeadColorBuf = mHeadCapColorByteBuffer.asFloatBuffer();mHeadColorBuf.position(0);mHeadCapColorBufferPos = 0;}/**1、了解线条开始的方向,将半径线条绕旋转该方向与标准测量用向量的夹角的角度量* 2、旋转180度时按照一定步进产生多个顶点,todo 但怎么确定旋转的方向是顺时针还是逆时针?以什么为依据判断?以传入向量方向为参考,但具体怎么做?*/float initVert[] = new float[] { //初始时左端点的坐标,初始时在原点两侧,然后以传入的顶点作为偏移量-mLineWidth / 2f, 0};//旋转并在过程中产生顶点float actualVec[] = new float[3];actualVec[0] = secVec[0] - firstVec[0];actualVec[1] = secVec[1] - firstVec[1];double angle = calcAngleOfVectorsOnXYPanel(mStandardVec, actualVec); //对比基准向量旋转了多少度int step = 6; //改成只有90度可以得到一个尖头笔帽List<float[]> newVecs = new LinkedList<>();if (!isHead) {//给曲线结尾加一段和线宽等长的边lineCapAddBorder(angle, firstVec, newVecs);}//半圆线头for (double degreeBias = angle; degreeBias <= 180 + angle; degreeBias += step) {try {float rotatedVec[] = rotate2d(initVert, degreeBias);float newVec[] = new float[6];//偏移到对应位置newVec[0] = rotatedVec[0] + firstVec[0];newVec[1] = rotatedVec[1] + firstVec[1];newVec[3] += firstVec[0];newVec[4] += firstVec[1];newVecs.add(newVec);} catch (Exception e) {e.printStackTrace();}}if (isHead) {//给曲线开头加一段和线宽等长的边lineCapAddBorder(angle, firstVec, newVecs);}for (float[] newVec : newVecs) {for (int i = 0; i < newVec.length; i++) {checkCapacity();mPointBuf.put(mPointBufferPos++, newVec[i]);}for (int i = 0; i < newVec.length / 3; i++) {checkCapacity();//写入颜色值r,g,b,afloat alpha = (float) (((color & 0xFF000000) >> 24) & 0x000000FF) / 255f;float blue = (float) ((color & 0x000000FF)) / 255f;float green = (float) ((color & 0x0000FF00) >> 8) / 255f;float red = (float) ((color & 0x00FF0000) >> 16) / 255f;mColorBuf.put(mColorBufferPos++, red);mColorBuf.put(mColorBufferPos++, green);mColorBuf.put(mColorBufferPos++, blue);mColorBuf.put(mColorBufferPos++, alpha);}}return newVecs.size() * newVecs.get(0).length;}
最后效果:
旋转步进设定为90度,因此能显示尖头效果:

设定为15度,则可以形成非常圆润的线头:

以线条方式绘制,即可看到顶点构成如下图:

基本再现了Android path + paint的大部分线条效果了。
相关文章:
GL绘制自定义线条3_自定义线帽
安卓Path搭配Paint可以设置线帽,我想能不能把我自己的线条绘制Demo也加上类似的功能。 线头规则描述: 1、设一个线宽一半的线段,坐标为(0, 0)到(-lineWidth / 2, 0)。 2、设步骤1的线段有一垂直于它的向量(0,1),然后传…...
【AGC】新版鸿蒙崩溃SDK集成使用方法
【背景】 我们知道AGC的Crash SDK都是需要强制集成华为分析SDK的,在使用时的崩溃数据上报都要依靠分析服务来完成,这就容易受到限制,有时出现无数据的情况就要依次排查崩溃SDK与分析SDK,比较麻烦。而就在不久前,鸿蒙崩…...
vue-7:组件库(移动端vant)(PC端element)
移动端vant 插件安装(按需导入) 重启生效 # 通过 npm 安装 npm i unplugin-vue-components -D# 通过 yarn 安装 yarn add unplugin-vue-components -D 导入基于 vite 的项目: 如果是基于 vite 的项目,在 vite.config.js 文件中…...
JavaScript中splice()、slice()、split()三种方法的区别,及使用详细
简介:splice、slice、split是JavaScript中,比较常用的三个数组方法,表面看起来有点相像,用处却大不相同,今天就来分别介绍下它们的用法。 1、splice()方法 splice方法可以用来删除数组中的元素,或者向数组…...
Linux更新操作系统Openssh版本9.3p1(源码编译安装)
Linux更新操作系统Openssh版本9.3p1(源码编译安装) 部署前准备 安装依赖 yum install -y gcc gcc-c glibc make autoconf openssl openssl-devel pcre-devel pam-develyum install -y pam* zlib* openssh-9.3p1.tar.gzopenssl-3.1.0.tar.gz备份文件 cp /etc/pam.d/sshd /etc/…...
MS COCO数据集介绍
MS COCO数据集介绍 MS COCO全称是Microsoft Common Objects in Context,是由微软开发维护的大型图像数据集,包括不同检测任务: Object Detection([主要处理人、车、大象等]) DensePose(姿态密度检测&…...
Java之线程池
目录 一.上节复习 1.阻塞队列 二.线程池 1.什么是线程池 2.为什么要使用线程池 3.JDK中的线程池 三.工厂模式 1.工厂模式的目的 四.使用线程池 1.submit()方法 2.模拟两个阶段任务的执行 五.自定义一个线程池 六.JDK提供线程池的详解 1.如何自定义一个线程池? 2.创…...
让你的网站变得更智能 - B2 Pro主题问答模块新增OpenAI ChatGPT机器人自动回答功能
作为一个网站管理员,你一定会希望能够给你的用户提供更多、更好的服务。那么,你是否曾经想过为你的B2 Pro主题问答模块新增一个智能机器人自动回答功能呢?相信你一定想要这个功能,因为它能够大大提升你网站的用户体验。 现在,我们为你提供了一个好消息。我们已经为B2 Pro…...
仓库信息管理系统设计与实现
一、数据库设计 1.数据库模型设计概览 2.数据库表设计 ①depository 描述: 该表存储仓库的信息,比如仓库名称,仓库地址和仓库介绍 表结构: 序号 字段名 数据类型 主键 非空 默认值 描述 1 id INT(10) 是 是 2…...
初识Java多线程编程
文章目录 一、线程的状态二、线程的常见属性三、多线程编程Thread类常用构造方法1.继承Thread类2.实现Runnable接口3.匿名内部类实现4.lambda 表达式创建 Runnable 子类对象 四、线程的常见方法 一、线程的状态 //线程的状态是一个枚举类型 Thread.State public class ThreadS…...
最新入河排污口设置论证、水质影响预测与模拟、污水处理工艺分析及典型建设项目入河排污口方案报告书实例分析
随着水资源开发利用量不断增大,全国废污水排放量与日俱增,部分河段已远远超出水域纳污能力。近年来,部分沿岸入河排污口设置不合理,超标排污、未经同意私设排污口等问题逐步显现,已威胁到供水安全、水环境安全和水生态安全&#x…...
awk指令的详细指南
目录 工作原理 命令格式 awk常见的内建变量(可直接用)如下所示 按行输出文本 按字段输出文本 通过管道、双引号调用 Shell 命令 示例 CPU使用率 数组 编辑统计文件的内容出现的次数 使用awk 统计secure 访问日志中每个客户端IP的出现次数? …...
解密Netty中的Reactor模式
文章目录 单线程Reactor模式多线程Reactor模式Reactor模式中IO事件的处理流程Netty中的通道ChannelNetty中的反应器ReactorNetty中的处理器HandlerNetty中的通道Channel和处理器Handler的协作组件Pipeline Reactor(反应器)模式是高性能网络编程在设计和架构方面的基础模式.Doug…...
这是一个黑科技:C++爬虫~(文末报名C/C++领域新星计划)
目录 写在前面 完整代码 这里必看!! 写在最后 写在前面 现在所有人都知道万能的Python可以做机器学习,可以做人工智能,可以爬取各种小网站,但是你不知道,基于C++的正则表达式早就能够爬取各种网络数据啦!!你没猜错,阿玥将在这篇文章中简介怎么用C...
2023 年第八届数维杯数学建模挑战赛 赛题浅析
为了更好地让大家本次数维杯比赛选题,我将对本次比赛的题目进行简要浅析。本次比赛的选题中,研究生、本科组请从A、B题中任选一个 完成答卷,专科组请从B、C题中任选一个完成答卷。这也暗示了本次比赛的难度为A>B>C 选题人数初步估计也…...
Spring Boot单元测试
什么是单元测试? 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证的过程就叫单元测试。 单元测试是开发人员编写的一小段代码,用于检验被测代码的一个很小的、很明确的(代码) 功能是否正确。执行单元测试就是为了证明某…...
实景三维浪潮翻涌,新技术“席卷”石家庄!
5月11日,“全自主、全流程、全覆盖”2023实景三维新技术研讨会石家庄站暨航测与遥感学术交流会在石家庄凯旋金悦大酒店圆满举行。 本次会议由中国测绘学会、中国地理信息产业协会指导,河北省测绘学会、河北省地理信息产业协会主办,武汉大势智…...
【Python】使用小脚本
本文整理了我在学习和工作中用到的实用python脚本,希望也能帮助到需要的小伙伴~ 文章目录 视频格式转换顺序遍历文件夹中的文件 视频格式转换 安装视频处理库moviepy pip install moviepy安装FFmpeg(FFmpeg是一个开源的多媒体框架,moviepy…...
技术日志2023-5-18
1、Java远程调试 可参考:https://kefeng.wang/2018/03/06/idea-remote-debug/ 2、用户中心这样的基础项目有什么用,感觉非常鸡肋。 今天开发讨论中涉及到了用户中心,感觉在项目中使用用户中心只是给业务系统发一个token,业务系…...
JUC之锁
公平锁、非公平锁 公平锁指的是多线程按照申请锁的顺序来获取锁; 非公平锁指的是多线程不按照申请锁的顺序来获取锁。可能会出现优先级反转(后者居上) 公平锁为了保证线程申请顺序,势必要付出一定的性能代价,因此其吞…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
