{"id":17723808,"url":"https://github.com/adil192/one_dollar_unistroke_recognizer","last_synced_at":"2025-03-14T05:32:03.415Z","repository":{"id":204064770,"uuid":"710996192","full_name":"adil192/one_dollar_unistroke_recognizer","owner":"adil192","description":"The $1 Unistroke Recognizer, a 2D single-stroke recognizer ported to Flutter/Dart","archived":false,"fork":false,"pushed_at":"2024-07-08T14:47:30.000Z","size":7740,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-08-22T23:34:58.679Z","etag":null,"topics":["dart","flutter","gesture-recognition","shape-recognition","unistroke"],"latest_commit_sha":null,"homepage":"https://adil192.github.io/one_dollar_unistroke_recognizer","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adil192.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-27T23:16:51.000Z","updated_at":"2024-07-08T14:45:57.000Z","dependencies_parsed_at":"2023-11-16T07:23:10.071Z","dependency_job_id":"da78ea65-65ab-4497-bfe3-0f4fb04f9d7d","html_url":"https://github.com/adil192/one_dollar_unistroke_recognizer","commit_stats":null,"previous_names":["adil192/one_dollar_unistroke_recognizer"],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adil192%2Fone_dollar_unistroke_recognizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adil192%2Fone_dollar_unistroke_recognizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adil192%2Fone_dollar_unistroke_recognizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adil192%2Fone_dollar_unistroke_recognizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adil192","download_url":"https://codeload.github.com/adil192/one_dollar_unistroke_recognizer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243532517,"owners_count":20306151,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","flutter","gesture-recognition","shape-recognition","unistroke"],"created_at":"2024-10-25T15:44:04.913Z","updated_at":"2025-03-14T05:32:02.745Z","avatar_url":"https://github.com/adil192.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"A Dart port of the\n[$1 Unistroke Recognizer](https://depts.washington.edu/acelab/proj/dollar/index.html)\nwith some enhancements:\n- The [Protractor enhancement](https://dl.acm.org/doi/10.1145/1753326.1753654)\n- A way to get a \"perfect\" (canonical) shape from the user's stroke\n- A [better suited algorithm](https://en.wikipedia.org/wiki/Mean_absolute_error) for detecting straight lines\n- Type safety (with the `DefaultUnistrokeNames` enum, or another if you're using custom unistrokes)\n\nBy default it recognizes lines, circles, rectangles, and triangles.\nBut you can also recognize any custom unistrokes (see below).\n\nI'm building and maintaining this package for my open source note-taking app,\n[Saber](https://github.com/saber-notes/saber).\n\n[![Pub](https://img.shields.io/pub/v/one_dollar_unistroke_recognizer.svg)](https://pub.dev/packages/one_dollar_unistroke_recognizer)\n[![BSD 3-Clause License](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](LICENSE)\n\n## Usage\n\n#### Basic usage\n\n```dart\nfinal points = \u003cOffset\u003e[...];\nfinal recognized = recognizeUnistroke(points);\nif (recognized == null) {\n  print('No match found');\n} else {\n  // e.g. DefaultUnistrokeNames.circle\n  print('Stroke recognized as ${recognized.name}');\n}\n```\n\n#### Protractor enhancement\n\nThe Protractor enhancement is enabled by default. You can disable it by setting `useProtractor` to `false`.\n\n```dart\nfinal recognized = recognizeUnistroke(\n  points,\n  useProtractor: false,\n);\n```\n\n#### Getting the \"perfect\" (canonical) shape\n\nYou can get a \"perfect\" shape from the user's stroke by calling one of the following methods on the `RecognizedUnistroke` object:\n- `convertToCanonicalPolygon()`: Returns the closest template match, scaled and translated to match the input gesture. Note that this method returns a list of points, instead of a perfect circle or rectangle like the other methods. (Shown in 🔴red in the examples below.)\n- `convertToLine()`: Returns the first and last input points. (Shown in 🟠orange in the examples below.)\n- `convertToCircle()`: Returns the radius and center of the best-fit circle. (Shown in 🔵blue in the examples below.)\n- `convertToOval()`: The same as `convertToCircle()` but doesn't take the average of the width and height. (Not shown in the examples below.)\n- `convertToRect()`: Returns the `Rect` of the best-fit (bounding box) rectangle. Tip: you can round the corners of the Rect with `RRect.fromRectAndRadius`. (Shown in 🟢green in the examples below.)\n\n\u003c!-- Show examples from the test/goldens folder in a table --\u003e\n| Line | Circle | Rectangle | Triangle | Star |\n| -- | -- | -- | -- | -- |\n| ![Line](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/line.png) | ![Circle](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/circle.png) | ![Rectangle](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/rectangle.png) | ![Triangle](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/triangle.png) | ![Star](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/star.png) |\n| ![Line HQ](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/line_hq.png) | ![Circle HQ](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/circle_hq.png) | ![Rectangle HQ](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/rectangle_hq.png) | ![Triangle HQ](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/triangle_hq.png) | ![Star HQ](https://raw.githubusercontent.com/adil192/one_dollar_unistroke_recognizer/main/test/goldens/star_hq.png) |\n\n```dart\nfinal recognized = recognizeUnistroke(points);\nswitch (recognized?.name) {\n  case null:\n    break;\n  case DefaultUnistrokeNames.line:\n    final (start, end) = recognized!.convertToLine();\n    canvas.drawLine(start, end, paint);\n  case DefaultUnistrokeNames.circle:\n    final (center, radius) = recognized!.convertToCircle();\n    canvas.drawCircle(center, radius, paint);\n  case DefaultUnistrokeNames.rectangle:\n    final rect = recognized!.convertToRect();\n    if (youWantARoundedRectangle) {\n      canvas.drawRRect(\n        RRect.fromRectAndRadius(rect, Radius.circular(10)),\n        paint,\n      );\n    } else {\n      canvas.drawRect(rect, paint);\n    }\n  case DefaultUnistrokeNames.triangle:\n  case DefaultUnistrokeNames.star:\n    final polygon = recognized!.convertToCanonicalPolygon();\n    canvas.drawPoints(PointMode.polygon, polygon, paint);\n}\n```\n\n#### Using custom unistroke templates\n\nYou can recognize custom unistrokes by setting the `referenceUnistrokes` list.\n\nNote that this will disable the default unistroke templates defined in `default$1Unistrokes`.\n\nIf your key type isn't `DefaultUnistrokeNames`, you'll need to call\n`recognizeCustomUnistroke\u003cMyKey\u003e(...)` instead of `recognizeUnistroke()`,\nwhich will return a `RecognizedCustomUnistroke\u003cMyKey\u003e` instead of a\n`RecognizedUnistroke`.\n\nAlso note that straight lines are a special case in the default unistroke templates,\nand aren't available by default with custom unistroke templates.\nTo recognize straight lines, see the [Straight lines](#straight-lines) section below.\n\n```dart\nenum MyUnistrokeNames {\n  circle,\n  rectangle,\n  triangle,\n  leaf,\n}\n\nreferenceUnistrokes = \u003cUnistroke\u003cMyUnistrokeNames\u003e\u003e[\n  Unistroke(MyUnistrokeNames.circle, [...]),\n  Unistroke(MyUnistrokeNames.rectangle, [...]),\n  Unistroke(MyUnistrokeNames.triangle, [...]),\n  Unistroke(MyUnistrokeNames.leaf, [...]),\n];\n\nfinal recognized = recognizeCustomUnistroke\u003cMyUnistrokeNames\u003e(points);\n```\n\nAlternatively, you can temporarily override the `referenceUnistrokes` list for a single call to `recognizeUnistroke` by setting the `overrideReferenceUnistrokes` list.\n\n```dart\nfinal recognized = recognizeCustomUnistroke\u003cMyUnistrokeNames\u003e(\n  points,\n  overrideReferenceUnistrokes: [...],\n);\n```\n\n#### Straight lines\n\nStraight lines are a special case in the default unistroke templates,\nin that they're best recognized by a different algorithm than the other shapes (i.e. not $1).\n\nIf you're using `default$1Unistrokes` (the default), you don't need to worry about this, and straight lines will be detected as `DefaultUnistrokeNames.line`.\n\nBut if you're using custom unistroke templates,\nensure that your straight line unistroke template has exactly 2 points and that they're distinct.\n\n```dart\nreferenceUnistrokes = \u003cUnistroke\u003cMyUnistrokeNames\u003e\u003e[\n  Unistroke(MyUnistrokeNames.line, [\n    // This template should have exactly 2 distinct points.\n    Offset(0, 0),\n    Offset(0, 100),\n  ]),\n  // ...\n];\n```\n\n## About the $1 Unistroke Recognizer\n\nThe $1 Unistroke Recognizer is a 2-D single-stroke recognizer designed for rapid prototyping of gesture-based user interfaces. In machine learning terms, $1 is an instance-based nearest-neighbor classifier with a 2-D Euclidean distance function, i.e., a geometric template matcher. $1 is a significant extension of the proportional shape matching approach used in SHARK2, which itself is an adaptation of Tappert's elastic matching approach with zero look-ahead. Despite its simplicity, $1 requires very few templates to perform well and is only about 100 lines of code, making it easy to deploy. An optional enhancement called Protractor improves $1's speed. \n\nYou can read more about the $1 Unistroke Recognizer at [depts.washington.edu/acelab/proj/dollar](https://depts.washington.edu/acelab/proj/dollar/index.html).\n\nOn that link, you'll see a demo with 16 templates. This package uses a different set of templates by default, but you can use the original templates by setting `referenceUnistrokes` to `example$1Unistrokes`.\n\nThis Dart package is a port of the JavaScript version of the $1 Unistroke Recognizer, which you can find at [depts.washington.edu/acelab/proj/dollar/dollar.js](https://depts.washington.edu/acelab/proj/dollar/dollar.js).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadil192%2Fone_dollar_unistroke_recognizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadil192%2Fone_dollar_unistroke_recognizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadil192%2Fone_dollar_unistroke_recognizer/lists"}