惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Simon Willison's Weblog
Simon Willison's Weblog
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Spread Privacy
Spread Privacy
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Engineering at Meta
Engineering at Meta
T
Tenable Blog
C
Cisco Blogs
T
The Blog of Author Tim Ferriss
NISL@THU
NISL@THU
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
Secure Thoughts
N
News and Events Feed by Topic
Google DeepMind News
Google DeepMind News
博客园 - 三生石上(FineUI控件)
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
H
Hacker News: Front Page
I
InfoQ
L
LangChain Blog
Security Latest
Security Latest
The Cloudflare Blog
Forbes - Security
Forbes - Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Stack Overflow Blog
Stack Overflow Blog
TaoSecurity Blog
TaoSecurity Blog
量子位
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Visual Studio Blog
Scott Helme
Scott Helme
爱范儿
爱范儿
A
Arctic Wolf
F
Full Disclosure
酷 壳 – CoolShell
酷 壳 – CoolShell
Schneier on Security
Schneier on Security
N
News and Events Feed by Topic
C
CXSECURITY Database RSS Feed - CXSecurity.com
L
LINUX DO - 最新话题
V2EX - 技术
V2EX - 技术
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
人人都是产品经理
人人都是产品经理
The Hacker News
The Hacker News
Hugging Face - Blog
Hugging Face - Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
Application and Cybersecurity Blog
Application and Cybersecurity Blog

isaced

使用 TypeScript 构建的 appstore-connect-sdk 归档 我用 SwiftUI 写了一个 V2ex 客户端 Docker 容器化多应用部署 Swift Server-side SDWebImage 为什么无法读取 webpmux 生成的 WebP 动图? [译] 探索 Swift 4 中新的 String API 对一个 Django 站点做 ab 压力测试 fir-mac 开发笔记 iOS10 Safari 引导用户信任企业签名 Swift Web 开发之 Vapor - 模版 Leaf(三) Swift Web 开发之 Vapor - 路由(二) HoloLens 初体验 Swift Web 开发之 Vapor - 入门(一) Jenkins for iOS 搭建日记 iOS HOOK 注入与重签名 Xcode 使用 Configuration 来配置不同的项目环境 图床从SAE迁移到七牛 给 UIProgressView 脱掉那层微弱的渐变 初用 CocoaAsyncSocket 记一次 SQLite 性能优化 有多色 - 开发小记 海岛风情 - 泰国普吉岛六日游 EGOCache 源码解析 [iOS] UITextView 追加文字自动跳到顶部解决方法 Cocoa 新的依赖管理工具:Carthage 新年快乐! 博客主机换到搬瓦工 写了个iOS小游戏:有多色 iOS 后台任务之 Long-running background task 悄悄的就必须用 XCode 5.1.1 上传了! HTTP Content-type 与 AFNetworking Objective-Cloud 初体验(1) 掌中站上架小记 NSLoger 上线了 Python Markdown 做语法限制 Nginx + Gunicorn + Django 部署小记 用Shell脚本批量裁剪App Icon各个尺寸 LaunchRocket使用小记 用宏提速NSCoding 2014新年伊始:近期读过的好文推荐 [iOS] stringWithFormat 和 initWithFormat 有何不同? Apache + Flask + mod_wsgi + Koding 部署小记 收集几个Objective-C的HTML解析库 NSMapTable: 不只是一个能放weak指针的 NSDictionary 2013,再见! Emlog到Typecho的迁移工具:emlog2typecho [iOS]给上拉下拉刷新PullToRefresh做本地化 [iOS]代码推荐:大图缩放视图 - Vertigo iOS:hidesBottomBarWhenPushed的正确用法 Xcode 5新的Interface Builder,你准备好了吗? [翻译]如何使用Django创建简单的博客 我的Mac音乐播放器 初识 NSDataDetector Objective-C 计算代码运行时间 WordPress上传文件自动重命名 iOS:仿jing.fm的音乐播放视图 我的啃黑苹果之路 关于 搜索 项目
日语五十音假名临摹校验算法
2015-02-27 · via isaced

这是一篇躺在笔记本里接近两年的技术笔记,今天正好想起了,编搬过来分享一下

这是在学校的时候做日语五十音 APP 的临摹模块中一个技术点,当时也是纠结了一阵子才总结出方法的,遂记录以纪念之~

需求

大致需求是要在手机上实现类似传统毛笔字临摹字帖练习的功能,画出暗隐的底文,然后每一划的 起笔点落笔点 给出红圈提示,规定起落在指定红圈区域中,才算正确,并给出下一划的提示,以达到简单的正确性临摹检测。当然,这里只是简单的只识别首尾锚点(红圈),如果要增强正确性检测,可以每一笔多增加几个检测锚点。

预览

五十音临摹预览

原理

  • 每个假名最多 4 笔,也就是最多 8 个点
  • 开始的时候提示第一个落笔点,触摸后提示第二个点,也就是第一笔点结束点,松开后判断两个点是否在范围(圆圈)内,如果都在点话,表示这一笔临摹大致正确,取消第一笔点这两个提示圈,然后就开始第二笔,如此循环…
  • 要实现此功能肯定要有一个标志变量来保存进度,也就是写到第几笔了,不然写到后面会干涉前面已经正确的笔画。
  • 还需要事先录入所有需要临摹的字符检验点坐标集合,当时另外做了一个小工具,用于自动生成 / 坐标并输出至文件以便程序读取 (如果手动取点每个字符也太折腾了)

实现

  • 用的【ACEDrawingView】来作临摹板,稍作修改,添加两个协议函数来返回出开始触摸的坐标和起指(抬起)的坐标。 把所有的点保存到一个数组,对应每个点的提示圆圈也保存到另一个数组。
  • 一个标识变量(okNumber)存储当前写到第几笔了。

代码

-(void)touchBegan:(CGPoint)point
{
    beganPoint = point;

    if (okNumber == 0) {
        if ([self isInRound:point and:[pointArray[0] CGPointValue] in:kRadius]) {
            [self.drawingView addSubview:roundArray[1]];
        }
    }else if (okNumber == 1)
    {
        if ([self isInRound:point and:[pointArray[2] CGPointValue] in:kRadius]) {
            [self.drawingView addSubview:roundArray[3]];
        }
    }else if (okNumber == 2)
    {
        if ([self isInRound:point and:[pointArray[4] CGPointValue] in:kRadius]) {
            [self.drawingView addSubview:roundArray[5]];
        }
    }
}

-(void)touchEnded:(CGPoint)point
{
    if (okNumber == 0) {
        BOOL b1 = [self isInRound:beganPoint and:[pointArray[0] CGPointValue] in:kRadius];
        BOOL b2 = [self isInRound:point and:[pointArray[1] CGPointValue] in:kRadius];

        if ( b1 && b2 ) {
            okNumber=1;
            [roundArray[0] removeFromSuperview];
            [roundArray[1] removeFromSuperview];
            [self.drawingView addSubview:roundArray[2]];
        }
        else{
            [self.drawingView undoLatestStep];
        }
    }else if (okNumber == 1){
        BOOL b1 = [self isInRound:beganPoint and:[pointArray[2] CGPointValue] in:kRadius];
        BOOL b2 = [self isInRound:point and:[pointArray[3] CGPointValue] in:kRadius];

        if ( b1 && b2 ) {
            okNumber=2;
            [roundArray[2] removeFromSuperview];
            [roundArray[3] removeFromSuperview];
            [self.drawingView addSubview:roundArray[4]];
        }else{
            [self.drawingView undoLatestStep];
        }

    }else if (okNumber == 2){
        BOOL b1 = [self isInRound:beganPoint and:[pointArray[4] CGPointValue] in:kRadius];
        BOOL b2 = [self isInRound:point and:[pointArray[5] CGPointValue] in:kRadius];

        if ( b1 && b2 ) {
            okNumber=3;
            [roundArray[4] removeFromSuperview];
            [roundArray[5] removeFromSuperview];
            self.yinLabel.hidden=YES;
        }else{
            [self.drawingView undoLatestStep];
        }

    }
    else{
        [self.drawingView undoLatestStep];
    }
}

然后做了一下算法优化,用循环实现,更灵活,效率更高,代码更清晰:

-(void)touchBegan:(CGPoint)point
{
    beganPoint = point;

    for (int i=0; i<[pointArray count]/2; i++) {
        if (okNumber == i) {
            if ([self isInRound:point and:[pointArray[i*2] CGPointValue] in:kRadius]) {
                [self.drawingView addSubview:roundArray[i*2+1]];
                return;
            }
        }
    }
}

-(void)touchEnded:(CGPoint)point
{

    for (int i=0; i<[pointArray count]/2; i++) {
        if (okNumber == i) {
            BOOL b1 = [self isInRound:beganPoint and:[pointArray[i*2] CGPointValue] in:kRadius];
            BOOL b2 = [self isInRound:point and:[pointArray[i*2+1] CGPointValue] in:kRadius];

            if ( b1 && b2 ) {
                okNumber++;
                [roundArray[i*2] removeFromSuperview];
                [roundArray[i*2+1] removeFromSuperview];
                if (i+1 != [pointArray count]/2) {
                    [self.drawingView addSubview:roundArray[i*2+2]];
                }

                return;
            }
            else{
                [self.drawingView undoLatestStep];
            }

        }
    }
}