├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ ├── feature_request.md │ └── new_feature.md ├── .gitignore ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── Editor.meta ├── Editor ├── FontImporter.cs ├── FontImporter.cs.meta ├── SVGImporter.cs ├── SVGImporter.cs.meta ├── VX.GPUVectorGraphics.Editor.asmdef └── VX.GPUVectorGraphics.Editor.asmdef.meta ├── Gizmos.meta ├── Gizmos ├── FontIcon.png └── FontIcon.png.meta ├── LICENSE ├── Pictures~ └── simple_bezier_curve.png ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── BezierProperties.cs ├── BezierProperties.cs.meta ├── CDT.meta ├── CDT │ ├── CDT.Constrain.cs │ ├── CDT.Constrain.cs.meta │ ├── CDT.ConstraintUtil.cs │ ├── CDT.ConstraintUtil.cs.meta │ ├── CDT.Primitive.cs │ ├── CDT.Primitive.cs.meta │ ├── CDT.QuadraticInsert.cs │ ├── CDT.QuadraticInsert.cs.meta │ ├── CDT.Triangulate.cs │ ├── CDT.Triangulate.cs.meta │ ├── CDT.TriangulationUtil.cs │ ├── CDT.TriangulationUtil.cs.meta │ ├── CDT.Util.cs │ ├── CDT.Util.cs.meta │ ├── CDT.cs │ └── CDT.cs.meta ├── CubicBezier.meta ├── CubicBezier │ ├── CubicBezier.CurveType.cs │ ├── CubicBezier.CurveType.cs.meta │ ├── CubicBezier.Geometry.cs │ ├── CubicBezier.Geometry.cs.meta │ ├── CubicBezier.Triangulate.cs │ └── CubicBezier.Triangulate.cs.meta ├── Font.meta ├── Font │ ├── FontCurve.cs │ ├── FontCurve.cs.meta │ ├── Glyph.cs │ ├── Glyph.cs.meta │ ├── Reader.meta │ ├── Reader │ │ ├── FontReader.cs │ │ ├── FontReader.cs.meta │ │ ├── FontReaderFile.cs │ │ └── FontReaderFile.cs.meta │ ├── Tables.meta │ ├── Tables │ │ ├── CMap.cs │ │ ├── CMap.cs.meta │ │ ├── Glyf.cs │ │ ├── Glyf.cs.meta │ │ ├── Head.cs │ │ ├── Head.cs.meta │ │ ├── Loca.cs │ │ ├── Loca.cs.meta │ │ ├── MaxP.cs │ │ ├── MaxP.cs.meta │ │ ├── Table.cs │ │ └── Table.cs.meta │ ├── TypeFace.cs │ └── TypeFace.cs.meta ├── QuadraticBezier.meta ├── VGMath.cs ├── VGMath.cs.meta ├── VX.GPUVectorGraphics.Runtime.asmdef └── VX.GPUVectorGraphics.Runtime.asmdef.meta ├── Shader.meta ├── Shader ├── CubicBezier.shadersubgraph ├── CubicBezier.shadersubgraph.meta ├── CubicBezierCurve.shader ├── CubicBezierCurve.shader.meta ├── QuadraticBezier.shadersubgraph ├── QuadraticBezier.shadersubgraph.meta ├── QuadraticBezierCurve.shader ├── QuadraticBezierCurve.shader.meta ├── SimpleLitQuadraticBezier.shadergraph └── SimpleLitQuadraticBezier.shadergraph.meta ├── package.json └── package.json.meta /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: voxelltech 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ["paypal.me/voxelltechnologies"] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new_feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New Feature 3 | about: Creating a new feature for this project. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Summary** 11 | 12 | A quick intro or summary on the feature you want to create. 13 | 14 | **Intended Outcome** 15 | 16 | - What is the use case of this? 17 | - How will it affect/benefit the project? 18 | 19 | **How will it work?** 20 | 21 | - How to use this feature? 22 | 23 | *finally, please assign yourself to this issue if you intend to work on this new feature thanks!* 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | __pycache__/ 3 | checkpoints/ 4 | inference/ 5 | env/ 6 | temp/ 7 | 8 | # This .gitignore file should be placed at the root of your Unity project directory 9 | # 10 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 11 | # 12 | /[Ll]ibrary/ 13 | /[Tt]emp/ 14 | /[Oo]bj/ 15 | /[Bb]uild/ 16 | /[Bb]uilds/ 17 | /[Ll]ogs/ 18 | /[Uu]ser[Ss]ettings/ 19 | /[Rr]ecordings/ 20 | /[Rr]eleases/ 21 | /[Rr]elease/ 22 | 23 | 24 | # MemoryCaptures can get excessive in size. 25 | # They also could contain extremely sensitive data 26 | /[Mm]emoryCaptures/ 27 | 28 | # Asset meta data should only be ignored when the corresponding asset is also ignored 29 | !/[Aa]ssets/**/*.meta 30 | 31 | # Uncomment this line if you wish to ignore the asset store tools plugin 32 | # /[Aa]ssets/AssetStoreTools* 33 | 34 | # Autogenerated Jetbrains Rider plugin 35 | /[Aa]ssets/Plugins/Editor/JetBrains* 36 | 37 | # Visual Studio cache directory 38 | .vs/ 39 | 40 | # Gradle cache directory 41 | .gradle/ 42 | 43 | # Autogenerated VS/MD/Consulo solution and project files 44 | ExportedObj/ 45 | .consulo/ 46 | *.csproj 47 | *.unityproj 48 | *.sln 49 | *.suo 50 | *.tmp 51 | *.user 52 | *.userprefs 53 | *.pidb 54 | *.booproj 55 | *.svd 56 | *.pdb 57 | *.mdb 58 | *.opendb 59 | *.VC.db 60 | 61 | # Unity3D generated meta files 62 | *.pidb.meta 63 | *.pdb.meta 64 | *.mdb.meta 65 | 66 | # Unity3D generated file on crash reports 67 | sysinfo.txt 68 | 69 | # Builds 70 | *.apk 71 | *.aab 72 | *.unitypackage 73 | *.unitypackage.meta 74 | *.exe 75 | 76 | # Crashlytics generated file 77 | crashlytics-build.properties 78 | 79 | # Packed Addressables 80 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 81 | 82 | # Temporary auto-generated Android Assets 83 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 84 | /[Aa]ssets/[Ss]treamingAssets/aa/* 85 | 86 | # Tensorflow trained checkpoints and weights 87 | *.h5 88 | *.h5.meta 89 | *.pbmm 90 | *.pbmm.meta 91 | *.tflite 92 | *.tflite.meta 93 | 94 | # API keys 95 | *.apikey 96 | 97 | # pycaches 98 | *.pyc 99 | 100 | # audio files 101 | AudioClips/ 102 | *.mp3 103 | *.wav 104 | *.audio 105 | 106 | # license 107 | LICENSE.meta 108 | 109 | # large libraries 110 | native/ 111 | tensorflow.dll 112 | tensorflow.dll.meta -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.3.3] 2 | 3 | ### New Features 4 | 5 | - `FontCurve` stores an array of precomputed meshes on import. 6 | - Implementation of binary search to search for character's glyph/mesh index. 7 | 8 | ### Changes 9 | 10 | - Burst Compile uses high precision float and strict float mode. 11 | - Reduced `MARGIN` from 10.0f to 1.0f. 12 | - Renamed `Circumcenter` to `Circumcircle`. 13 | - Renamed `GenerateMeshDataFromGlyph` to `ExtractGlyphData`. 14 | - Increased `FontImporter` verion from 2 to 3. 15 | - Instead of `charMaps`, `FontCurve` now stores 2 separate arrays: 16 | - `charCodes`: an array of supported characters. 17 | - `glyphIndices`: an array of glyph indices corresponding to the supported character codes. 18 | - Uses "super-triangle" instead of "rect-triangles" as there is an elegant way to create a huge triangle that encapsulates all points inside. 19 | 1. Calculate bounding-box (min/max rect) of the points. 20 | 2. Place the bounding-box on a line. 21 | ``` 22 | __ 23 | |__| 24 | ---- 25 | ``` 26 | 3. Place 2 more of the same box at the top and bottom of the current box. 27 | ``` 28 | __ 29 | |__| 30 | __ 31 | |__| 32 | __ 33 | |__| 34 | ---- 35 | ``` 36 | 4. Place another 2 more of the same box at the left and right of the bottom most box. 37 | ``` 38 | __ 39 | |__| 40 | __ 41 | |__| 42 | __ __ __ 43 | |__| |__| |__| 44 | -------------- 45 | ``` 46 | 5. Now, connect the "left-bottom most" point, "right-bottom most" point, and the "middle-top-most" point together. 47 | ``` 48 | __ /\ 49 | |__| /__\ 50 | __ / __ \ 51 | |__| / |__| \ 52 | __ __ __ _/ __ \_ 53 | |__| |__| |__| |/_| |__| |_\| 54 | ---------------------------------- 55 | ``` 56 | 6. And now you get a "super-triangle" that encapsulates all the points in the middle box! (of course, you can add some kind of margin to the box itself to enlarge the "super-triangle" just to make sure!) 57 | - Added "alpha" to package version. 58 | 59 | ### Bug Fixes 60 | 61 | - `PointInTriangle` method in `VGMath` is much more accurate (to handle edge cases) and computationally cheap. 62 | - Removed addition of `EPSILON` to `div` in `Cirumcircle` to prevent numerical inaccuracy. 63 | - `TriEdgeIntersect` method now checks for similarity based on point position rather than index as there might be duplicated points that have a different index. 64 | 65 | ## [0.3.2] 66 | 67 | ### Changes 68 | 69 | - Major code refactoring for CDT triangulation and constraint: 70 | - Generalize procedures into static function calls. 71 | - Create functions to prevent repetitive code. 72 | - CDT utility functions are now separated into 3 files: 73 | - `CDT.Util` for general utility purposes. 74 | - `CDT.TriangulationUtil` for delaunay triangulation utility purposes. 75 | - `CDT.ConstraintUtil` for constrained delaunay triangulation utility purposes. 76 | 77 | ### Bug Fixes 78 | 79 | - Removed `Debug.Log` calls from a static function that is being called in a burst compiled code. 80 | 81 | ## [0.3.1] 82 | 83 | ### New Features 84 | 85 | - Removal of triangles that are outside the constraint contour. 86 | 87 | ### Changes 88 | 89 | - Uses "rect-triangles" instead of "super-triangles" for stability. This can prevent some points from being excluded when the min and max rect is large. 90 | 91 | ## [0.3.0] 92 | 93 | ### New Features 94 | 95 | - Implementation of constrained delaunay triangulation. 96 | 97 | ### Changes 98 | 99 | - Removed `Voxell.GPUVectorGraphics.Delaunay` namespace. 100 | 101 | ## [0.2.0] 102 | 103 | ### New Features 104 | 105 | - Bezier Properties for describing a shape made up of bezier curves. 106 | - Open Font lightweight importer: 107 | - Native support. 108 | - Store data as glyphs. Each glyph will have a glyph contour containing a sequence of points in the order of p0-ctrl0-p1-ctrl1. 109 | - Character maps that maps each character to a glyph index. 110 | 111 | ### Bug Fixes 112 | 113 | - Fixes uvw coordinate when being flipped. 114 | - Prevent extra triangulation when encountering loop artifacts. 115 | 116 | ## [0.1.0] 117 | 118 | - Initial release. -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 144cf53156b805e4ea68518bd4c7528e 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fa7bb127083dcdc4bb6fc95551ad49fd 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/FontImporter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using UnityEngine; 4 | using UnityEditor.AssetImporters; 5 | using Unity.Mathematics; 6 | using Unity.Collections; 7 | using Unity.Jobs; 8 | 9 | namespace Voxell.GPUVectorGraphics.Font 10 | { 11 | using Inspector; 12 | 13 | [ScriptedImporter(3, new[] { "ttfvector", "otfVector" }, new[] { "ttf", "otf" })] 14 | public class FontImporter : ScriptedImporter 15 | { 16 | /// Format of the font. 17 | public enum FontFormat 18 | { 19 | /// True type format. 20 | TTF, 21 | /// Open type format. 22 | OTF 23 | } 24 | 25 | public const int FormatTrueType = 0x00010000; 26 | public const int FormatOTF = 0x4F54544F; 27 | 28 | [Tooltip("Format of the font."), InspectOnly] 29 | public FontFormat fontFormat; 30 | [Tooltip("0x00010000 (TTF) or 0x4F54544F (OTF)."), InspectOnly] 31 | public uint sfntVersion; 32 | [Tooltip("Number of tables."), InspectOnly] 33 | public ushort numTables; 34 | 35 | private Dictionary _tableMap; 36 | 37 | // we convert the integer into a float for easier division operation purposes 38 | // we want a float division instead of a integer division (which looses all the floating point data) 39 | [Tooltip("The *integer value for a unit distance in the font."), InspectOnly] 40 | public float unitsPerEm = 0; 41 | [Tooltip("Offset width, relevant when certain peices of data in the file."), InspectOnly] 42 | public int offsetByteWidth = 0; 43 | [Tooltip("The number of glyphs in the font file."), InspectOnly] 44 | public int glyphCount; 45 | 46 | [Tooltip("Name of the type face."), InspectOnly] 47 | public string fontName; 48 | 49 | public override void OnImportAsset(AssetImportContext ctx) 50 | { 51 | string filePath = FileUtilx.GetAssetFilePath(ctx.assetPath); 52 | FontReaderFile fontReader = new FontReaderFile(filePath); 53 | fontReader.SetPosition(0); 54 | fontReader.ReadInt(out sfntVersion); 55 | 56 | 57 | if (sfntVersion != FormatTrueType && sfntVersion != FormatOTF) 58 | { 59 | Debug.LogWarning("Font type not supported!"); 60 | return; 61 | } 62 | 63 | fontFormat = sfntVersion == FormatTrueType ? FontFormat.TTF : FontFormat.OTF; 64 | fontReader.ReadInt(out numTables); 65 | fontReader.SetPosition(6, SeekOrigin.Current); 66 | 67 | Table table; 68 | _tableMap = new Dictionary(); 69 | for (int t=0; t < numTables; ++t) 70 | { 71 | table = new Table(); 72 | table.Read(fontReader); 73 | _tableMap.Add(table.tag, table); 74 | } 75 | 76 | // head will tell us all table offset information 77 | //////////////////////////////////////////////////////////////////////////////// 78 | #region Head 79 | if (!_tableMap.TryGetValue(Head.TagName, out table)) 80 | { 81 | Debug.LogError("Font file does not have a header!"); 82 | return; 83 | } 84 | 85 | fontReader.SetPosition(table.offset); 86 | Head head = new Head(); 87 | head.Read(fontReader); 88 | unitsPerEm = (float)head.unitsPerEm; 89 | offsetByteWidth = head.OffsetByteWidth; 90 | #endregion 91 | 92 | // maxp will tell us how many glyphs there are in the file 93 | //////////////////////////////////////////////////////////////////////////////// 94 | #region Maxp 95 | if (!_tableMap.TryGetValue(Maxp.TagName, out table)) 96 | { 97 | Debug.LogError("Font file does not have maxp data!"); 98 | return; 99 | } 100 | 101 | fontReader.SetPosition(table.offset); 102 | Maxp maxP = new Maxp(); 103 | maxP.Read(fontReader); 104 | glyphCount = maxP.numGlyphs; 105 | #endregion 106 | 107 | // loca knows offsets of glyphs in the glyf table 108 | //////////////////////////////////////////////////////////////////////////////// 109 | #region Loca 110 | if (!_tableMap.TryGetValue(Loca.TagName, out table)) 111 | { 112 | Debug.LogError("Font file does not have loca data!"); 113 | return; 114 | } 115 | 116 | fontReader.SetPosition(table.offset); 117 | Loca loca = new Loca(); 118 | loca.Read(fontReader, glyphCount, offsetByteWidth == 4); 119 | #endregion 120 | 121 | // glyf provides contour data 122 | //////////////////////////////////////////////////////////////////////////////// 123 | #region Glyf 124 | if (!_tableMap.TryGetValue(Glyf.TagName, out table)) 125 | { 126 | Debug.LogError("Font file does not have glyf data!"); 127 | return; 128 | } 129 | 130 | Glyph[] glyphs = new Glyph[glyphCount]; 131 | fontName = FileUtilx.GetFilename(filePath).Split('.')[0]; 132 | for (int g=0; g < glyphCount; g++) 133 | { 134 | uint glyphOffset = loca.GetGlyphOffset(table, g); 135 | uint glyphSize = loca.GetGlyphSize(g); 136 | // possibly a blank space or character does not exsit in this font 137 | // usually we treat it as a blank box 138 | if (glyphSize == 0) continue; 139 | 140 | fontReader.SetPosition(glyphOffset); 141 | Glyf glyf = new Glyf(); 142 | glyf.Read(fontReader); 143 | int contourCount = glyf.numberOfContours; 144 | 145 | if (glyf.IsComplex) 146 | { 147 | // complex 148 | int compositeCount = glyf.compositeEntries.Count; 149 | glyphs[g].compositeReferences = new Glyph.CompositeReference[compositeCount]; 150 | for (int c=0; c < compositeCount; c++) 151 | { 152 | Glyf.CompositeEntry ce = glyf.compositeEntries[c]; 153 | Glyph.CompositeReference cref = new Font.Glyph.CompositeReference(); 154 | cref.xAxis = new float2(ce.xscale, ce.scale01); 155 | cref.yAxis = new float2(ce.scale10, ce.yscale); 156 | cref.offset = new float2(ce.argument1, ce.argument2) / unitsPerEm; 157 | cref.glyphRef = ce.glyphIndex; 158 | 159 | glyphs[g].compositeReferences[c] = cref; 160 | } 161 | glyphs[g].isComplex = true; 162 | } else 163 | { 164 | // simple 165 | glyphs[g].contours = new QuadraticContour[contourCount]; 166 | int pointIdx = 0; 167 | 168 | for (int c=0; c < contourCount; c++) 169 | { 170 | int endPoint = glyf.endPtsOfCountours[c]+1; 171 | int pointCount = endPoint - pointIdx; 172 | 173 | // initialize with the minimum capacity needed (if there are no consecutive points) 174 | List points = new List(pointCount); 175 | List isControls = new List(pointCount); 176 | 177 | // populate lists with original data 178 | for (; pointIdx < endPoint; pointIdx++) 179 | { 180 | isControls.Add((glyf.simpflags[pointIdx] & Glyf.ON_CURVE_POINT) == 0); 181 | float2 point = new float2( 182 | (float)glyf.xCoordinates[pointIdx], (float)glyf.yCoordinates[pointIdx] 183 | ) / unitsPerEm; 184 | 185 | points.Add(point); 186 | } 187 | 188 | // insert missing vertex points and control points 189 | for (int p=0; p < points.Count; p++) 190 | { 191 | // reverts back to 0 when we reach the end of the array 192 | int nextIdx = (p + 1) % points.Count; 193 | 194 | if (isControls[p] && isControls[nextIdx]) 195 | { 196 | // If 2 control points are next to each other, there's an implied 197 | // point in between them at their average. 198 | // We add them explicitly for better parallelization in the future. 199 | 200 | // average vector between previous point and current point 201 | float2 avgPoint = (points[p] + points[nextIdx]) * 0.5f; 202 | points.Insert(nextIdx, avgPoint); 203 | isControls.Insert(nextIdx, false); 204 | } else if (!isControls[p] && !isControls[nextIdx]) 205 | { 206 | // If 2 vertex points are next to each other, it represents that 207 | // the segment is a line instead of a curve. 208 | // We add a dummy control point which has the exact same location 209 | // as the current vertex point to indicate that it is a line. 210 | 211 | points.Insert(nextIdx, points[p]); 212 | isControls.Insert(nextIdx, true); 213 | } 214 | } 215 | 216 | // convert point list into segment array 217 | int segmentCount = points.Count/2; 218 | QuadraticPathSegment[] segments = new QuadraticPathSegment[segmentCount]; 219 | for (int s=0; s < segmentCount; s++) 220 | { 221 | int segmentIdx = s*2; 222 | segments[s].p0 = points[segmentIdx]; 223 | segments[s].p1 = points[segmentIdx+1]; 224 | } 225 | 226 | // store to glyph struct 227 | glyphs[g].contours[c].segments = segments; 228 | glyphs[g].contours[c].closed = true; 229 | glyphs[g].maxRect = new float2(glyf.xMax, glyf.yMax)/unitsPerEm; 230 | glyphs[g].minRect = new float2(glyf.xMin, glyf.yMin)/unitsPerEm; 231 | } 232 | glyphs[g].isComplex = false; 233 | } 234 | } 235 | #endregion 236 | 237 | // cmap tells us the mapping between 238 | // character codes and glyph indices used throughout the font file 239 | //////////////////////////////////////////////////////////////////////////////// 240 | #region cmap 241 | if (!_tableMap.TryGetValue(CMap.TagName, out table)) 242 | { 243 | Debug.LogError("Font file does not have cmap data!"); 244 | return; 245 | } 246 | 247 | fontReader.SetPosition(table.offset); 248 | CMap cMap = new CMap(); 249 | cMap.Read(fontReader, table.offset); 250 | 251 | Dictionary characterRemap = null; 252 | foreach (CMap.CharacterConversionMap ccm in cMap.EnumCharacterMaps()) 253 | { 254 | Dictionary potentialMap = ccm.MapCodeToIndex(fontReader); 255 | if (characterRemap == null || potentialMap.Count > characterRemap.Count) 256 | characterRemap = potentialMap; 257 | } 258 | 259 | List charCodesList = new List(); 260 | charCodesList.Add(0); 261 | List glyphIndicesList = new List(); 262 | glyphIndicesList.Add(0); 263 | 264 | foreach (KeyValuePair kvp in characterRemap) 265 | { 266 | int key = (int) kvp.Key; 267 | int value = (int) kvp.Value; 268 | 269 | if (value == 0 || value >= glyphCount) continue; 270 | charCodesList.Add(key); 271 | glyphIndicesList.Add(value); 272 | } 273 | 274 | int[] charCodes = charCodesList.ToArray(); 275 | int[] glyphIndices = glyphIndicesList.ToArray(); 276 | 277 | System.Array.Sort(charCodes, glyphIndices); 278 | // sort character codes 279 | #endregion 280 | 281 | _tableMap.Clear(); 282 | fontReader.Close(); 283 | 284 | FontCurve fontCurve = ScriptableObject.CreateInstance(); 285 | ctx.AddObjectToAsset("FontCurve", fontCurve); 286 | 287 | // create mesh for each char 288 | //////////////////////////////////////////////////////////////////////////////// 289 | int keyCount = charCodes.Length; 290 | Mesh[] meshes = new Mesh[keyCount]; 291 | NativeArray jobHandles = new NativeArray(keyCount, Allocator.Temp); 292 | 293 | NativeArray[] na_points_array = 294 | new NativeArray[keyCount]; 295 | NativeList[] na_triangles_array = 296 | new NativeList[keyCount]; 297 | NativeArray[] na_contours_array = 298 | new NativeArray[keyCount]; 299 | 300 | for (int k=0; k < keyCount; k++) 301 | { 302 | Glyph glyph = glyphs[glyphIndices[k]]; 303 | if (glyph.contours == null) continue; 304 | int contourCount = glyph.contours.Length; 305 | if (contourCount == 0) continue; 306 | 307 | float2[] points; 308 | CDT.ContourPoint[] contours; 309 | FontCurve.ExtractGlyphData(in glyph, out points, out contours); 310 | 311 | float2 maxRect = glyph.maxRect; 312 | float2 minRect = glyph.minRect; 313 | jobHandles[k] = CDT.ConstraintTriangulate( 314 | minRect, maxRect, in points, in contours, 315 | out na_points_array[k], 316 | out na_triangles_array[k], 317 | out na_contours_array[k] 318 | ); 319 | 320 | Debug.Log(glyphIndices[k]); 321 | Debug.Log((char)charCodes[k]); 322 | jobHandles[k].Complete(); 323 | } 324 | 325 | // make sure that all the scheduled jobs are completed 326 | // JobHandle.CompleteAll(jobHandles); 327 | 328 | for (int k=0; k < keyCount; k++) 329 | { 330 | if (!na_points_array[k].IsCreated) continue; 331 | Glyph glyph = glyphs[glyphIndices[k]]; 332 | 333 | string name = ((char)charCodes[k]).ToString(); 334 | Mesh mesh = new Mesh(); 335 | mesh.name = name; 336 | mesh.SetVertices(FontCurve.PointsToVertices(in na_points_array[k])); 337 | mesh.SetIndices(na_triangles_array[k], MeshTopology.Triangles, 0); 338 | mesh.RecalculateNormals(); 339 | mesh.RecalculateTangents(); 340 | 341 | float3 maxRect = new float3(glyph.maxRect, 0.0f); 342 | float3 minRect = new float3(glyph.minRect, 0.0f); 343 | float3 size = maxRect - minRect; 344 | float3 center = minRect + size*0.5f; 345 | mesh.bounds = new Bounds(center, size); 346 | 347 | meshes[k] = mesh; 348 | ctx.AddObjectToAsset(name, mesh); 349 | } 350 | 351 | fontCurve.Initialize(glyphs, charCodes, glyphIndices, meshes); 352 | 353 | // dispose all allocated arrays 354 | jobHandles.Dispose(); 355 | for (int k=0; k < keyCount; k++) 356 | { 357 | if(na_points_array[k].IsCreated) 358 | na_points_array[k].Dispose(); 359 | if(na_triangles_array[k].IsCreated) 360 | na_triangles_array[k].Dispose(); 361 | if(na_contours_array[k].IsCreated) 362 | na_contours_array[k].Dispose(); 363 | } 364 | } 365 | } 366 | } -------------------------------------------------------------------------------- /Editor/FontImporter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 629c9a55b73b7db4087275fa513e57b4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/SVGImporter.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using UnityEditor.AssetImporters; 4 | 5 | namespace Voxell.GPUVectorGraphics.Editor 6 | { 7 | [ScriptedImporter(0, "svg")] 8 | public class SVGImporter : ScriptedImporter 9 | { 10 | public override void OnImportAsset(AssetImportContext ctx) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Editor/SVGImporter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7da1bf49bbe64584f9c109c30646df2e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/VX.GPUVectorGraphics.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VX.GPUVectorGraphics.Editor", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:da829e25e5d77364984881df942d9735", 6 | "GUID:b23efe0d0bd83184681bd63517e3c327", 7 | "GUID:d8b63aba1907145bea998dd612889d6b", 8 | "GUID:e0cd26848372d4e5c891c569017e11f1", 9 | "GUID:8a2eafa29b15f444eb6d74f94a930e1d" 10 | ], 11 | "includePlatforms": [ 12 | "Editor" 13 | ], 14 | "excludePlatforms": [], 15 | "allowUnsafeCode": false, 16 | "overrideReferences": false, 17 | "precompiledReferences": [], 18 | "autoReferenced": true, 19 | "defineConstraints": [], 20 | "versionDefines": [], 21 | "noEngineReferences": false 22 | } -------------------------------------------------------------------------------- /Editor/VX.GPUVectorGraphics.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f896bb4f5ed8a3742b1e02b52b203edf 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Gizmos.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 192563b8914f43f4494627e7bfb423c6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Gizmos/FontIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixon-voxell/UnityGPUVectorGraphics/85a4b4282a83856a3edd92572c0e44d8ef89c893/Gizmos/FontIcon.png -------------------------------------------------------------------------------- /Gizmos/FontIcon.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f04b41271e371d047854280baa7d3658 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 0 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 1 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 1 40 | wrapV: 1 41 | wrapW: 0 42 | nPOTScale: 0 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 1 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 1 55 | spriteTessellationDetail: -1 56 | textureType: 2 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | platformSettings: 67 | - serializedVersion: 3 68 | buildTarget: DefaultTexturePlatform 69 | maxTextureSize: 1024 70 | resizeAlgorithm: 0 71 | textureFormat: -1 72 | textureCompression: 1 73 | compressionQuality: 50 74 | crunchedCompression: 0 75 | allowsAlphaSplitting: 0 76 | overridden: 0 77 | androidETC2FallbackOverride: 0 78 | forceMaximumCompressionQuality_BC6H_BC7: 0 79 | - serializedVersion: 3 80 | buildTarget: Standalone 81 | maxTextureSize: 64 82 | resizeAlgorithm: 0 83 | textureFormat: -1 84 | textureCompression: 1 85 | compressionQuality: 50 86 | crunchedCompression: 0 87 | allowsAlphaSplitting: 0 88 | overridden: 0 89 | androidETC2FallbackOverride: 0 90 | forceMaximumCompressionQuality_BC6H_BC7: 0 91 | - serializedVersion: 3 92 | buildTarget: iPhone 93 | maxTextureSize: 8192 94 | resizeAlgorithm: 0 95 | textureFormat: -1 96 | textureCompression: 1 97 | compressionQuality: 50 98 | crunchedCompression: 0 99 | allowsAlphaSplitting: 0 100 | overridden: 0 101 | androidETC2FallbackOverride: 0 102 | forceMaximumCompressionQuality_BC6H_BC7: 0 103 | - serializedVersion: 3 104 | buildTarget: Android 105 | maxTextureSize: 8192 106 | resizeAlgorithm: 0 107 | textureFormat: -1 108 | textureCompression: 1 109 | compressionQuality: 50 110 | crunchedCompression: 0 111 | allowsAlphaSplitting: 0 112 | overridden: 0 113 | androidETC2FallbackOverride: 0 114 | forceMaximumCompressionQuality_BC6H_BC7: 0 115 | - serializedVersion: 3 116 | buildTarget: Windows Store Apps 117 | maxTextureSize: 8192 118 | resizeAlgorithm: 0 119 | textureFormat: -1 120 | textureCompression: 1 121 | compressionQuality: 50 122 | crunchedCompression: 0 123 | allowsAlphaSplitting: 0 124 | overridden: 0 125 | androidETC2FallbackOverride: 0 126 | forceMaximumCompressionQuality_BC6H_BC7: 0 127 | - serializedVersion: 3 128 | buildTarget: Server 129 | maxTextureSize: 64 130 | resizeAlgorithm: 0 131 | textureFormat: -1 132 | textureCompression: 1 133 | compressionQuality: 50 134 | crunchedCompression: 0 135 | allowsAlphaSplitting: 0 136 | overridden: 0 137 | androidETC2FallbackOverride: 0 138 | forceMaximumCompressionQuality_BC6H_BC7: 0 139 | spriteSheet: 140 | serializedVersion: 2 141 | sprites: [] 142 | outline: [] 143 | physicsShape: [] 144 | bones: [] 145 | spriteID: 5e97eb03825dee720800000000000000 146 | internalID: 0 147 | vertices: [] 148 | indices: 149 | edges: [] 150 | weights: [] 151 | secondaryTextures: [] 152 | nameFileIdTable: {} 153 | spritePackingTag: 154 | pSDRemoveMatte: 0 155 | pSDShowRemoveMatteOption: 0 156 | userData: 157 | assetBundleName: 158 | assetBundleVariant: 159 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 Voxell Technologies 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Pictures~/simple_bezier_curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixon-voxell/UnityGPUVectorGraphics/85a4b4282a83856a3edd92572c0e44d8ef89c893/Pictures~/simple_bezier_curve.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity GPU Vector Graphics 2 | 3 | ![Simple Bezier Curve](./Pictures~/simple_bezier_curve.png) 4 | 5 | This is a GPU accelerated vector graphics library built specifially for the Unity3D engine. 6 | 7 | You can find the example repository [here](https://github.com/nixon-voxell/UnityGPUVectorGraphicsExamples). 8 | 9 | - [Unity GPU Vector Graphics](#unity-gpu-vector-graphics) 10 | - [Installation](#installation) 11 | - [Support the project!](#support-the-project) 12 | - [Join the community!](#join-the-community) 13 | - [License](#license) 14 | - [References](#references) 15 | 16 | ## Installation 17 | 18 | External dependencies: 19 | 20 | - voxell.util ([UnityUtil](https://github.com/voxell-tech/UnityUtil)) 21 | 22 | 1. Clone the [UnityUtil](https://github.com/voxell-tech/UnityUtil) repository into your `Packages` folder. 23 | 2. Clone this repository into your `Packages` folder. 24 | 3. And you are ready to go! 25 | 26 | ## Support the project! 27 | 28 | 29 | patreon 30 | 31 | 32 | 33 | kofi 34 | 35 | 36 | ## Join the community! 37 | 38 | 39 | discord 40 | 41 | 42 | 43 | discord 44 | 45 | 46 | 47 | ## License 48 | 49 | This repository as a whole is licensed under the Apache License 2.0. Individual files may have a different, but compatible license. 50 | 51 | See [license file](./LICENSE) for details. 52 | 53 | ## References 54 | 55 | 1. [Gliss (GitHub)](https://github.com/mdk/gliss) 56 | 2. [GPU Curve Rendering (GitHub)](https://github.com/azer89/GPU_Curve_Rendering) 57 | 3. [Beny_Core (GitHub)](https://github.com/Reavenk/Berny_Core) 58 | 4. [Resolution Independent Curve Rendering using Programmable Grpahics Hardware](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf) 59 | 5. [NVIDIA GPU Gem 3 - Rendering Vector Art on the GPU](https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-25-rendering-vector-art-gpu) 60 | 6. [Delaunay Voronoi (GitHub)](https://github.com/RafaelKuebler/DelaunayVoronoi) -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cfaca45a4f7847b4099d16da0be55f1a 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b5b0b8411bfea946a9f8a5299b49ac8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/BezierProperties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using Unity.Mathematics; 4 | 5 | namespace Voxell.GPUVectorGraphics 6 | { 7 | /// 8 | /// The various ways corners in an SVG file can be formed. 9 | /// 10 | public enum Corner 11 | { 12 | /// A half circle to round the end. 13 | Round, 14 | 15 | /// A flat connection. 16 | Bevel, 17 | 18 | /// Extrapolate sides with straight lines to where they collide. 19 | Miter, 20 | 21 | /// Extrapolate sides continuing their curve to where they collide. 22 | Arc 23 | } 24 | 25 | /// The style of an unconnected edge's end. 26 | public enum Cap 27 | { 28 | /// Stop instantly. 29 | Butt, 30 | 31 | /// Round it out with a half circle. 32 | Round, 33 | 34 | /// Add an additional half square, based off the width of the edge. 35 | Square 36 | } 37 | 38 | [Serializable] 39 | public struct CubicSegment 40 | { 41 | /// Origin point of the segment. 42 | public float2 p0; 43 | 44 | /// First control point of the segment. 45 | public float2 p1; 46 | 47 | /// Second control point of the segment. 48 | public float2 p2; 49 | 50 | /// Ending point of the segment. 51 | public float2 p3; 52 | 53 | public CubicSegment(float2 p0, float2 p1, float2 p2, float2 p3) 54 | { 55 | this.p0 = p0; 56 | this.p1 = p1; 57 | this.p2 = p2; 58 | this.p3 = p3; 59 | } 60 | } 61 | 62 | [Serializable] 63 | public struct QuadraticSegment 64 | { 65 | /// Origin point of the segment. 66 | public float2 p0; 67 | 68 | /// First control point of the segment. 69 | public float2 p1; 70 | 71 | /// Ending point of the segment. 72 | public float2 p2; 73 | 74 | public QuadraticSegment(float2 p0, float2 p1, float2 p2) 75 | { 76 | this.p0 = p0; 77 | this.p1 = p1; 78 | this.p2 = p2; 79 | } 80 | } 81 | 82 | [Serializable] 83 | public struct CubicPathSegment 84 | { 85 | /// Origin point of the segment. 86 | public float2 p0; 87 | 88 | /// First control point of the segment. 89 | public float2 p1; 90 | 91 | /// Second control point of the segment. 92 | public float2 p2; 93 | 94 | public CubicPathSegment(float2 p0, float2 p1, float2 p2) 95 | { 96 | this.p0 = p0; 97 | this.p1 = p1; 98 | this.p2 = p2; 99 | } 100 | } 101 | 102 | [Serializable] 103 | public struct QuadraticPathSegment 104 | { 105 | /// Origin point of the segment. 106 | public float2 p0; 107 | 108 | /// First control point of the segment. 109 | public float2 p1; 110 | 111 | public QuadraticPathSegment(float2 p0, float2 p1) 112 | { 113 | this.p0 = p0; 114 | this.p1 = p1; 115 | } 116 | } 117 | 118 | [Serializable] 119 | public struct CubicContour 120 | { 121 | /// An array of every cubic path segments on the contour. 122 | public CubicPathSegment[] segments; 123 | 124 | /// A closed loop contour. 125 | public bool closed; 126 | } 127 | 128 | [Serializable] 129 | public struct QuadraticContour 130 | { 131 | /// An array of every quadratic path segments on the contour. 132 | public QuadraticPathSegment[] segments; 133 | 134 | /// A closed loop contour. 135 | public bool closed; 136 | } 137 | 138 | [Serializable] 139 | public struct PathProperties 140 | { 141 | /// How the beginning of the path should be displayed. 142 | public Cap head; 143 | 144 | /// How the end of the path should be displayed. 145 | public Cap tail; 146 | 147 | /// How the corners of the path should be displayed. 148 | public Corner corner; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Runtime/BezierProperties.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c5f56c2c2ca25c4087b78a6ffe6d979 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CDT.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bddd1811271257b4fb17783336df7620 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/CDT/CDT.Constrain.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | using Unity.Collections; 3 | using Unity.Jobs; 4 | using Unity.Burst; 5 | 6 | namespace Voxell.GPUVectorGraphics 7 | { 8 | using Mathx; 9 | 10 | public partial class CDT 11 | { 12 | [BurstCompile(FloatPrecision.High, FloatMode.Strict)] 13 | private struct ConstrainJob : IJob 14 | { 15 | public NativeArray na_contours; 16 | public NativeArray na_points; 17 | public NativeList na_triangles; 18 | 19 | public ConstrainJob( 20 | ref NativeArray na_contours, 21 | ref NativeArray na_points, ref NativeList na_triangles 22 | ) 23 | { 24 | this.na_contours = na_contours; 25 | this.na_points = na_points; 26 | this.na_triangles = na_triangles; 27 | } 28 | 29 | public void Execute() 30 | { 31 | // create a list of all initial edges from the delaunay triangulation 32 | NativeList na_edges = new NativeList(Allocator.Temp); 33 | for (int t=0, triangleCount=na_triangles.Length/3; t < triangleCount; t++) 34 | { 35 | int t0, t1, t2; 36 | GetTriangleIndices(in na_triangles, t, out t0, out t1, out t2); 37 | Edge edge0 = new Edge(t0, t1); 38 | Edge edge1 = new Edge(t1, t2); 39 | Edge edge2 = new Edge(t2, t0); 40 | 41 | // make sure no duplicate edges are added 42 | if (!na_edges.Contains(edge0)) na_edges.Add(edge0); 43 | if (!na_edges.Contains(edge1)) na_edges.Add(edge1); 44 | if (!na_edges.Contains(edge2)) na_edges.Add(edge2); 45 | } 46 | 47 | NativeList na_insideIndices = new NativeList(2, Allocator.Temp); 48 | NativeList na_outsideIndices = new NativeList(2, Allocator.Temp); 49 | NativeList na_blackListedTris = new NativeList(Allocator.Temp); 50 | 51 | NativeList na_repairEdges = new NativeList(Allocator.Temp); 52 | NativeList na_blackListedRepairEdges = new NativeList(Allocator.Temp); 53 | NativeList na_repairTriangles = new NativeList(Allocator.Temp); 54 | NativeList na_repairCircumcenters = new NativeList(Allocator.Temp); 55 | 56 | int segmentCount = na_contours.Length - 1; 57 | for (int s=0; s < segmentCount; s++) 58 | { 59 | na_insideIndices.Clear(); 60 | na_outsideIndices.Clear(); 61 | na_blackListedTris.Clear(); 62 | 63 | ContourPoint c0 = na_contours[s]; 64 | ContourPoint c1 = na_contours[s + 1]; 65 | // only check for intersection if both points are in the same contour 66 | if (c0.contourIdx != c1.contourIdx) continue; 67 | 68 | int e0 = c0.pointIdx; 69 | int e1 = c1.pointIdx; 70 | 71 | // if edge already exist, continue to the next contour segment 72 | Edge edge = new Edge(e0, e1); 73 | if (na_edges.Contains(edge)) continue; 74 | 75 | // initialize point list with edge points 76 | na_insideIndices.Add(e0); na_insideIndices.Add(e1); 77 | na_outsideIndices.Add(e0); na_outsideIndices.Add(e1); 78 | // initialize min and max point with edge points 79 | float2x2 ePoints = new float2x2(na_points[e0], na_points[e1]); 80 | float2 minRect = math.min(ePoints[0], ePoints[1]); 81 | float2 maxRect = math.max(ePoints[0], ePoints[1]); 82 | // remove all blocking triangles 83 | for (int t=0, triangleCount=na_triangles.Length/3; t < triangleCount; t++) 84 | { 85 | int3 tIdx; 86 | GetTriangleIndices(in na_triangles, t, out tIdx.x, out tIdx.y, out tIdx.z); 87 | 88 | bool3 diff_t; 89 | float2x3 tPoints; 90 | if (TriEdgeIntersect(in na_points, in tIdx, in edge, in ePoints, out diff_t, out tPoints)) 91 | { 92 | // black list triangle to be removed later 93 | na_blackListedTris.Add(t); 94 | 95 | // sort points into outside and inside regions 96 | float2 n = math.normalize(float2x.perpendicular(ePoints[1] - ePoints[0])); 97 | for (int i=0; i < 3; i++) 98 | { 99 | if (diff_t[i]) 100 | { 101 | minRect = math.min(minRect, na_points[tIdx[i]]); 102 | maxRect = math.max(maxRect, na_points[tIdx[i]]); 103 | if (math.dot(n, tPoints[i] - ePoints[0]) < 0.0f) na_insideIndices.Add(tIdx[i]); 104 | else na_outsideIndices.Add(tIdx[i]); 105 | } 106 | } 107 | } 108 | } 109 | 110 | // remove all black listed triangles 111 | RemoveBlacklistedTriangles(in na_blackListedTris, ref na_triangles); 112 | 113 | // retriangulate inside points 114 | if (na_insideIndices.Length > 2) 115 | { 116 | TriangulatePoints( 117 | in minRect, in maxRect, in na_insideIndices, 118 | ref na_repairEdges, ref na_repairTriangles, ref na_repairCircumcenters, 119 | ref na_blackListedRepairEdges, ref na_blackListedTris 120 | ); 121 | } 122 | 123 | // retriangulate outside points 124 | if (na_outsideIndices.Length > 2) 125 | { 126 | TriangulatePoints( 127 | in minRect, in maxRect, in na_outsideIndices, 128 | ref na_repairEdges, ref na_repairTriangles, ref na_repairCircumcenters, 129 | ref na_blackListedRepairEdges, ref na_blackListedTris 130 | ); 131 | } 132 | } 133 | 134 | // remove triangles connected to a contour edge and is outside the contour 135 | //////////////////////////////////////////////////////////////////////////////// 136 | NativeMultiHashMap na_pointTriMap = new NativeMultiHashMap( 137 | na_points.Length, Allocator.Temp 138 | ); 139 | 140 | // create point idx to related triangles map 141 | for (int t=0, triangleCount=na_triangles.Length/3; t < triangleCount; t++) 142 | { 143 | int t0, t1, t2; 144 | GetTriangleIndices(in na_triangles, t, out t0, out t1, out t2); 145 | 146 | na_pointTriMap.Add(t0, t); 147 | na_pointTriMap.Add(t1, t); 148 | na_pointTriMap.Add(t2, t); 149 | } 150 | 151 | na_blackListedTris.Clear(); 152 | for (int s=0; s < segmentCount; s++) 153 | { 154 | ContourPoint c0 = na_contours[s]; 155 | ContourPoint c1 = na_contours[s + 1]; 156 | // only check for edge triangles if both points are in the same contour 157 | if (c0.contourIdx != c1.contourIdx) continue; 158 | 159 | int e0 = c0.pointIdx; 160 | int e1 = c1.pointIdx; 161 | 162 | Edge edge = new Edge(e0, e1); 163 | 164 | NativeMultiHashMap.Enumerator enumerator = na_pointTriMap.GetValuesForKey(e0); 165 | int2 tris, extraPoints; 166 | FindEdgeTriangleAndExtraPoint(in enumerator, in na_triangles, in edge, out tris, out extraPoints); 167 | 168 | // if there are 2 related triangles we remove the one that has an extra point that is outside the edge 169 | // we might encounter edges that has only 1 related triangle as it might 170 | // be removed during the constraint process above 171 | if (tris[1] != -1) 172 | { 173 | float2 n = math.normalize(float2x.perpendicular(na_points[edge.e1] - na_points[edge.e0])); 174 | 175 | for (int t=0; t < 2; t++) 176 | { 177 | if (math.dot(n, na_points[extraPoints[t]] - na_points[edge.e1]) > 0.0f) 178 | { 179 | if (!na_blackListedTris.Contains(tris[t])) 180 | na_blackListedTris.Add(tris[t]); 181 | // only 1 of the triangles are going to be removed 182 | break; 183 | } 184 | } 185 | } 186 | } 187 | 188 | // remove triangles that are not connected to 189 | // any contour edge and is outside the contour 190 | //////////////////////////////////////////////////////////////////////////////// 191 | NativeArray na_contourEdges = new NativeArray(na_contours.Length, Allocator.Temp); 192 | 193 | for (int s=0; s < segmentCount; s++) 194 | na_contourEdges[s] = new Edge(na_contours[s].pointIdx, na_contours[s+1].pointIdx); 195 | 196 | na_contourEdges[segmentCount] = new Edge( 197 | na_contours[segmentCount].pointIdx, na_contours[0].pointIdx 198 | ); 199 | 200 | { 201 | int removeCount = 0; 202 | for (int t=0, triangleCount=na_triangles.Length/3; t < triangleCount; t++) 203 | { 204 | int t0, t1, t2; 205 | GetTriangleIndices(in na_triangles, t-removeCount, out t0, out t1, out t2); 206 | 207 | Edge edge0 = new Edge(t0, t1); 208 | Edge edge1 = new Edge(t1, t2); 209 | Edge edge2 = new Edge(t2, t0); 210 | 211 | NativeMultiHashMap.Enumerator enum0 = na_pointTriMap.GetValuesForKey(t0); 212 | NativeMultiHashMap.Enumerator enum1 = na_pointTriMap.GetValuesForKey(t1); 213 | NativeMultiHashMap.Enumerator enum2 = na_pointTriMap.GetValuesForKey(t2); 214 | int2 tris0, tris1, tris2; 215 | FindEdgeTriangles(in enum0, in na_triangles, in edge0, out tris0); 216 | FindEdgeTriangles(in enum1, in na_triangles, in edge1, out tris1); 217 | FindEdgeTriangles(in enum2, in na_triangles, in edge2, out tris2); 218 | 219 | // triangles that are not connected to any contour 220 | if ( 221 | !na_contourEdges.Contains(edge0) && 222 | !na_contourEdges.Contains(edge1) && 223 | !na_contourEdges.Contains(edge2) 224 | ) 225 | { 226 | if ( 227 | na_blackListedTris.Contains(tris0[0]) || na_blackListedTris.Contains(tris0[1]) || 228 | na_blackListedTris.Contains(tris1[0]) || na_blackListedTris.Contains(tris1[1]) || 229 | na_blackListedTris.Contains(tris2[0]) || na_blackListedTris.Contains(tris2[1]) 230 | ) na_blackListedTris.Add(t); 231 | } 232 | } 233 | } 234 | 235 | // sort indices so that we remove the triangles with the lowest indices first 236 | na_blackListedTris.Sort(); 237 | // remove all black listed triangles 238 | { 239 | int removeCount = 0; 240 | for (int t=0, blackListedTriCount=na_blackListedTris.Length; t < blackListedTriCount; t++) 241 | RemoveTriangle(ref na_triangles, na_blackListedTris[t]-removeCount++); 242 | } 243 | 244 | // disposing all temp allocations 245 | //////////////////////////////////////////////////////////////////////////////// 246 | na_edges.Dispose(); 247 | 248 | na_insideIndices.Dispose(); 249 | na_blackListedTris.Dispose(); 250 | 251 | na_repairEdges.Dispose(); 252 | na_blackListedRepairEdges.Dispose(); 253 | na_repairTriangles.Dispose(); 254 | na_repairCircumcenters.Dispose(); 255 | 256 | na_pointTriMap.Dispose(); 257 | } 258 | 259 | /// Delaunay triangulate a portion of points defined by an indices array. 260 | /// min AABB point 261 | /// max AABB point 262 | /// indices array indicating the portion of points to be triangulated 263 | private void TriangulatePoints( 264 | in float2 minRect, in float2 maxRect, in NativeList na_indices, 265 | ref NativeList na_repairEdges, 266 | ref NativeList na_repairTriangles, 267 | ref NativeList na_repairCircumcenters, 268 | ref NativeList na_blackListedRepairEdges, 269 | ref NativeList na_blackListedTris 270 | ) 271 | { 272 | na_repairTriangles.Clear(); 273 | na_repairCircumcenters.Clear(); 274 | na_blackListedTris.Clear(); 275 | int idxCount = na_indices.Length; 276 | 277 | // create rect-triangle 278 | // CreateRectTriangle(in minRect, in maxRect, ref na_points, ref na_repairTriangles, ref na_repairCircumcenters); 279 | // create super-triangle 280 | CreateSuperTriangle(in minRect, in maxRect, ref na_points, ref na_repairTriangles, ref na_repairCircumcenters); 281 | 282 | for (int i=0; i < idxCount; i++) 283 | { 284 | na_repairEdges.Clear(); 285 | na_blackListedRepairEdges.Clear(); 286 | 287 | int pIdx = na_indices[i]; 288 | float2 point = na_points[pIdx]; 289 | 290 | // remove triangles that contains the current point in its circumcenter 291 | int removeCount = 0; 292 | for (int c=0, circumCount=na_repairCircumcenters.Length; c < circumCount; c++) 293 | { 294 | int cIdx = c-removeCount; 295 | Cirumcircle circumcenter = na_repairCircumcenters[cIdx]; 296 | if (circumcenter.ContainsPoint(point)) 297 | { 298 | int t0, t1, t2; 299 | GetTriangleIndices(in na_repairTriangles, cIdx, out t0, out t1, out t2); 300 | 301 | Edge edge = new Edge(t0, t1); 302 | AddEdgesOfRemovedTriangle(in edge, ref na_repairEdges, ref na_blackListedRepairEdges); 303 | 304 | edge.SetEdge(t1, t2); 305 | AddEdgesOfRemovedTriangle(in edge, ref na_repairEdges, ref na_blackListedRepairEdges); 306 | 307 | edge.SetEdge(t2, t0); 308 | AddEdgesOfRemovedTriangle(in edge, ref na_repairEdges, ref na_blackListedRepairEdges); 309 | 310 | RemoveTriAndCircum(ref na_repairCircumcenters, ref na_repairTriangles, cIdx); 311 | removeCount++; 312 | } 313 | } 314 | 315 | // sort black listed edge indices in ascending order and remove them 316 | na_blackListedRepairEdges.Sort(); 317 | RemoveBlacklistedEdges(in na_blackListedRepairEdges, ref na_repairEdges); 318 | 319 | // create new triangles out of it by connecting 320 | // each new edges to the current point 321 | CreateTrianglesForNewPoint( 322 | in pIdx, in point, in na_repairEdges, in na_points, 323 | ref na_repairTriangles, ref na_repairCircumcenters 324 | ); 325 | } 326 | 327 | // remove rect-triangle 328 | // RemoveRectTriangle(na_points.Length, ref na_repairTriangles); 329 | // remove super-triangle 330 | RemoveSuperTriangle(na_points.Length, ref na_repairTriangles); 331 | 332 | // remove triangles that are overlapping other existing triangles 333 | int repairTriCount=na_repairTriangles.Length/3; 334 | for (int rt=0; rt < repairTriCount; rt++) 335 | { 336 | int t0, t1, t2; 337 | GetTriangleIndices(in na_repairTriangles, rt, out t0, out t1, out t2); 338 | float2 triMidPoint = (na_points[t0] + na_points[t1] + na_points[t2]) * mathx.ONE_THIRD; 339 | 340 | for (int t=0, triangleCount=na_triangles.Length/3; t < triangleCount; t++) 341 | { 342 | GetTriangleIndices(in na_triangles, t, out t0, out t1, out t2); 343 | if (VGMath.PointInTriangle(triMidPoint, na_points[t0], na_points[t1], na_points[t2])) 344 | { 345 | na_blackListedTris.Add(rt); 346 | break; 347 | } 348 | } 349 | } 350 | 351 | // remove black listed triangles 352 | RemoveBlacklistedTriangles(in na_blackListedTris, ref na_repairTriangles); 353 | 354 | // add remaining triangles to the main triangle pool 355 | na_triangles.AddRange(na_repairTriangles); 356 | } 357 | } 358 | } 359 | } -------------------------------------------------------------------------------- /Runtime/CDT/CDT.Constrain.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f4ec1eea220e2bf4290ddd1f06b68780 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CDT/CDT.ConstraintUtil.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | using Unity.Collections; 3 | 4 | namespace Voxell.GPUVectorGraphics 5 | { 6 | public partial class CDT 7 | { 8 | /// 9 | /// Find the other triangle that is connected to this edge by looking up 10 | /// at a hash map from any of the point related to the edge. 11 | /// 12 | private static void FindEdgeTriangleAndExtraPoint( 13 | in NativeMultiHashMap.Enumerator enumerator, 14 | in NativeList na_triangles, 15 | in Edge edge, out int2 tris, out int2 extraPoints) 16 | { 17 | int foundCount = 0; 18 | tris = new int2(-1, -1); 19 | extraPoints = new int2(-1, -1); 20 | 21 | // UnityEngine.Debug.Log($"edge: {edge.e0}:{edge.e1}"); 22 | int t0, t1, t2; 23 | foreach (int t in enumerator) 24 | { 25 | GetTriangleIndices(in na_triangles, t, out t0, out t1, out t2); 26 | Edge edge0 = new Edge(t0, t1); 27 | Edge edge1 = new Edge(t1, t2); 28 | Edge edge2 = new Edge(t2, t0); 29 | 30 | if (edge.Equals(edge0) || edge.Equals(edge1) || edge.Equals(edge2)) 31 | { 32 | if (foundCount == 2) 33 | { 34 | UnityEngine.Debug.Log($"{foundCount}: {t0}, {t1}, {t2}"); 35 | UnityEngine.Debug.Log($"edge: {edge.e0}:{edge.e1}"); 36 | } 37 | tris[foundCount] = t; 38 | 39 | // find the odd one out (the point that is not related to the given edge) 40 | if (t0 != edge.e0 && t0 != edge.e1) extraPoints[foundCount] = t0; 41 | else if (t1 != edge.e0 && t1 != edge.e1) extraPoints[foundCount] = t1; 42 | else extraPoints[foundCount] = t2; 43 | 44 | foundCount++; 45 | } 46 | } 47 | } 48 | 49 | /// 50 | /// Find the other triangle that is connected to this edge by looking up 51 | /// at a hash map from any of the point related to the edge. 52 | /// 53 | private static void FindEdgeTriangles( 54 | in NativeMultiHashMap.Enumerator enumerator, 55 | in NativeList na_triangles, 56 | in Edge edge, out int2 tris) 57 | { 58 | int foundCount = 0; 59 | tris = new int2(-1, -1); 60 | 61 | // UnityEngine.Debug.Log($"edge: {edge.e0}:{edge.e1}"); 62 | int t0, t1, t2; 63 | foreach (int t in enumerator) 64 | { 65 | GetTriangleIndices(in na_triangles, t, out t0, out t1, out t2); 66 | Edge edge0 = new Edge(t0, t1); 67 | Edge edge1 = new Edge(t1, t2); 68 | Edge edge2 = new Edge(t2, t0); 69 | 70 | if (edge.Equals(edge0) || edge.Equals(edge1) || edge.Equals(edge2)) 71 | { 72 | if (foundCount == 2) 73 | { 74 | UnityEngine.Debug.Log($"{foundCount}: {t0}, {t1}, {t2}"); 75 | UnityEngine.Debug.Log($"edge: {edge.e0}:{edge.e1}"); 76 | } 77 | tris[foundCount++] = t; 78 | } 79 | } 80 | } 81 | 82 | /// Checks if an edge intersects a triangle. 83 | /// point pool 84 | /// triangle index 85 | /// edge indices 86 | /// 2 points that makes up the edge 87 | /// if triangle point is part of the edge 88 | /// triangle points 89 | private static bool TriEdgeIntersect( 90 | in NativeArray na_points, 91 | in int3 tIdx, in Edge edge, in float2x2 ePoints, 92 | out bool3 diff_t, out float2x3 tPoints 93 | ) 94 | { 95 | diff_t = new bool3(); 96 | tPoints = new float2x3(); 97 | for (int i=0; i < 3; i++) 98 | { 99 | tPoints[i] = na_points[tIdx[i]]; 100 | // compare point position rather than index 101 | // as there might be duplicated points with different index 102 | bool same = (tPoints[i].Equals(ePoints[0]) || tPoints[i].Equals(ePoints[1])); 103 | diff_t[i] = !same; 104 | } 105 | 106 | // only check for edge intersection when both edge are not connected 107 | // return a true, if either one of it intersects 108 | for (int i=0; i < 3; i++) 109 | { 110 | int nextIdx = (i + 1) % 3; 111 | if (diff_t[i] && diff_t[nextIdx]) 112 | if (VGMath.LinesIntersect(tPoints[i], tPoints[nextIdx], ePoints[0], ePoints[1])) 113 | return true; 114 | } 115 | 116 | return false; 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /Runtime/CDT/CDT.ConstraintUtil.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9297d0a6e1c40384db1eb8b5d0f9244c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CDT/CDT.Primitive.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | 3 | namespace Voxell.GPUVectorGraphics 4 | { 5 | public partial class CDT 6 | { 7 | private struct Edge : System.IEquatable 8 | { 9 | public int e0, e1; 10 | 11 | public Edge(int e0, int e1) 12 | { 13 | this.e0 = e0; 14 | this.e1 = e1; 15 | } 16 | 17 | public void SetEdge(int e0, int e1) 18 | { 19 | this.e0 = e0; 20 | this.e1 = e1; 21 | } 22 | 23 | public bool Equals(Edge other) 24 | => (this.e0 == other.e0 && this.e1 == other.e1) || 25 | (this.e0 == other.e1 && this.e1 == other.e0); 26 | } 27 | 28 | private struct Cirumcircle 29 | { 30 | public float2 center; 31 | public float sqradius; 32 | 33 | public Cirumcircle(float2 p0, float2 p1, float2 p2) 34 | { 35 | float dA = p0.x * p0.x + p0.y * p0.y; 36 | float dB = p1.x * p1.x + p1.y * p1.y; 37 | float dC = p2.x * p2.x + p2.y * p2.y; 38 | 39 | float aux1 = dA * (p2.y - p1.y) + dB * (p0.y - p2.y) + dC * (p1.y - p0.y); 40 | float aux2 = -(dA * (p2.x - p1.x) + dB * (p0.x - p2.x) + dC * (p1.x - p0.x)); 41 | float div = 2.0f * (p0.x * (p2.y - p1.y) + p1.x * (p0.y - p2.y) + p2.x * (p1.y - p0.y)); 42 | div = 1.0f / div; 43 | 44 | center = new float2(aux1, aux2) * div; 45 | sqradius = math.lengthsq(center - p0); 46 | } 47 | 48 | public bool ContainsPoint(float2 p) 49 | { 50 | float sqlength = math.lengthsq(center - p); 51 | return sqlength < (sqradius + math.EPSILON*2.0f); 52 | } 53 | } 54 | 55 | public struct ContourPoint 56 | { 57 | public int pointIdx; 58 | public int contourIdx; 59 | 60 | public ContourPoint(int pointIdx, int contourIdx) 61 | { 62 | this.pointIdx = pointIdx; 63 | this.contourIdx = contourIdx; 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /Runtime/CDT/CDT.Primitive.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f31b2987533090a44a05f2c845a75777 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CDT/CDT.QuadraticInsert.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | using Unity.Collections; 3 | using Unity.Jobs; 4 | using Unity.Burst; 5 | 6 | namespace Voxell.GPUVectorGraphics 7 | { 8 | using Mathx; 9 | 10 | public partial class CDT 11 | { 12 | // inserting quadratic control points from the contour 13 | [BurstCompile] 14 | private struct QuadraticInsertJob : IJob 15 | { 16 | public NativeArray na_points; 17 | public NativeArray na_controlPoints; 18 | public NativeList na_triangles; 19 | 20 | public void Execute() 21 | { 22 | NativeMultiHashMap na_pointTriMap = new NativeMultiHashMap( 23 | na_points.Length, Allocator.Temp 24 | ); 25 | 26 | // create point idx to related triangles map 27 | for (int t=0, triangleCount=na_triangles.Length/3; t < triangleCount; t++) 28 | { 29 | int t0, t1, t2; 30 | GetTriangleIndices(in na_triangles, t, out t0, out t1, out t2); 31 | 32 | na_pointTriMap.Add(t0, t); 33 | na_pointTriMap.Add(t1, t); 34 | na_pointTriMap.Add(t2, t); 35 | } 36 | 37 | for (int p=0, pointCount=na_controlPoints.Length; p < pointCount; p++) 38 | { 39 | float2 controlPoint = na_controlPoints[p]; 40 | // ignore if control point location is exactly same as the triangulation point 41 | if (na_points[p].Equals(controlPoint)) continue; 42 | 43 | // contour edge 44 | int nextP = (p + 1) % pointCount; 45 | Edge edge = new Edge(p, nextP); 46 | float2 p0 = na_points[edge.e0]; 47 | float2 p1 = na_points[edge.e1]; 48 | float2 n = math.normalize(float2x.perpendicular(p1 - p0)); 49 | 50 | // if point is outside the contour, directly triangulate it as it will not overlap any triangles 51 | if (math.dot(controlPoint - p0, n) > 0.0f) 52 | { 53 | // 54 | } else 55 | // if point is inside the contour, check for triangle intersections 56 | // remove them and then retriangulate them 57 | { 58 | NativeMultiHashMap.Enumerator enumerator0 = na_pointTriMap.GetValuesForKey(edge.e0); 59 | NativeMultiHashMap.Enumerator enumerator1 = na_pointTriMap.GetValuesForKey(edge.e1); 60 | 61 | // line 0: p0, controlPoint 62 | // line 1: p1, controlPoint 63 | 64 | NativeList na_intersectedTriangles = new NativeList(Allocator.Temp); 65 | 66 | foreach (int t in enumerator0) 67 | { 68 | int t0, t1, t2; 69 | GetTriangleIndices(in na_triangles, t, out t0, out t1, out t2); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Runtime/CDT/CDT.QuadraticInsert.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4a8c90f035c57fd4688a89fd0365efd7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CDT/CDT.Triangulate.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | using Unity.Collections; 3 | using Unity.Jobs; 4 | using Unity.Burst; 5 | 6 | namespace Voxell.GPUVectorGraphics 7 | { 8 | public partial class CDT 9 | { 10 | /// Bowyer-Watson delaunay triangulation. 11 | [BurstCompile(FloatPrecision.High, FloatMode.Strict)] 12 | private struct TriangulateJob : IJob 13 | { 14 | public float2 minRect; 15 | public float2 maxRect; 16 | 17 | public NativeArray na_points; 18 | public NativeList na_triangles; 19 | 20 | public TriangulateJob( 21 | float2 minRect, float2 maxRect, 22 | ref NativeArray na_points, 23 | ref NativeList na_triangles 24 | ) 25 | { 26 | this.minRect = minRect; 27 | this.maxRect = maxRect; 28 | 29 | this.na_points = na_points; 30 | this.na_triangles = na_triangles; 31 | } 32 | 33 | public void Execute() 34 | { 35 | // create temp arrays 36 | NativeList na_edges = new NativeList(Allocator.Temp); 37 | NativeList na_blackListedEdges = new NativeList(Allocator.Temp); 38 | NativeList na_cirumcircles = new NativeList(Allocator.Temp); 39 | 40 | // create rect-triangle 41 | // CreateRectTriangle(in minRect, in maxRect, ref na_points, ref na_triangles, ref na_cirumcircles); 42 | // create super-triangle 43 | CreateSuperTriangle(in minRect, in maxRect, ref na_points, ref na_triangles, ref na_cirumcircles); 44 | 45 | for (int p=0, pointCount=na_points.Length-3; p < pointCount; p++) 46 | { 47 | na_edges.Clear(); 48 | na_blackListedEdges.Clear(); 49 | 50 | float2 point = na_points[p]; 51 | 52 | // prevent duplicated points (only triangulate the first point found) 53 | int tempIdx = na_points.IndexOf(point); 54 | if (tempIdx != p) continue; 55 | 56 | // remove triangles that contains the current point in its circumcenter 57 | int removeCount = 0; 58 | for (int c=0, circumCount=na_cirumcircles.Length; c < circumCount; c++) 59 | { 60 | int idx = c - removeCount; 61 | Cirumcircle circumcenter = na_cirumcircles[idx]; 62 | if (circumcenter.ContainsPoint(point)) 63 | { 64 | int t0, t1, t2; 65 | GetTriangleIndices(in na_triangles, idx, out t0, out t1, out t2); 66 | 67 | Edge edge = new Edge(t0, t1); 68 | AddEdgesOfRemovedTriangle(in edge, ref na_edges, ref na_blackListedEdges); 69 | 70 | edge.SetEdge(t1, t2); 71 | AddEdgesOfRemovedTriangle(in edge, ref na_edges, ref na_blackListedEdges); 72 | 73 | edge.SetEdge(t2, t0); 74 | AddEdgesOfRemovedTriangle(in edge, ref na_edges, ref na_blackListedEdges); 75 | 76 | RemoveTriAndCircum(ref na_cirumcircles, ref na_triangles, idx); 77 | removeCount++; 78 | } 79 | } 80 | 81 | // sort black listed edge indices in ascending order and remove them 82 | na_blackListedEdges.Sort(); 83 | RemoveBlacklistedEdges(in na_blackListedEdges, ref na_edges); 84 | 85 | // create new triangles out of the current point 86 | // by connecting each new edges to the current point 87 | CreateTrianglesForNewPoint( 88 | in p, in point, in na_edges, in na_points, 89 | ref na_triangles, ref na_cirumcircles 90 | ); 91 | } 92 | 93 | // remove all triangles associated with the rect-triangle 94 | // RemoveRectTriangle(na_points.Length, ref na_triangles); 95 | // remove all triangles associated with the super-triangle 96 | RemoveSuperTriangle(na_points.Length, ref na_triangles); 97 | 98 | // dispose all temp allocations 99 | na_edges.Dispose(); 100 | na_blackListedEdges.Dispose(); 101 | na_cirumcircles.Dispose(); 102 | } 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /Runtime/CDT/CDT.Triangulate.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a7faf8ce4d8f7fd4a829934019be4df1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CDT/CDT.TriangulationUtil.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | using Unity.Collections; 3 | 4 | namespace Voxell.GPUVectorGraphics 5 | { 6 | public partial class CDT 7 | { 8 | /// Create super-triangle using the last 3 elements of the point array. 9 | private static void CreateSuperTriangle( 10 | in float2 minRect, in float2 maxRect, 11 | ref NativeArray na_points, 12 | ref NativeList na_triangles, 13 | ref NativeList na_cirumcircles 14 | ) 15 | { 16 | /// __ . /\ 17 | /// |__| . /__\ 18 | /// __ . / __ \ 19 | /// |__| . / |__| \ 20 | /// __ __ __ . _/ __ \_ 21 | /// |__| |__| |__| . |/_| |__| |_\| 22 | int pointCount = na_points.Length; 23 | float width = maxRect.x - minRect.x; 24 | float height = maxRect.y - minRect.y; 25 | float marginedWidth = width + MARGIN; 26 | float marginedHeight = height + MARGIN; 27 | 28 | int r0 = pointCount-3; 29 | int r1 = pointCount-2; 30 | int r2 = pointCount-1; 31 | 32 | // left bottom 33 | na_points[r0] = new float2(minRect.x - marginedWidth, minRect.y - marginedHeight); 34 | // right bottom 35 | na_points[r1] = new float2(maxRect.x + marginedWidth, minRect.y - marginedHeight); 36 | // top 37 | na_points[r2] = new float2(minRect.x + width*0.5f, maxRect.y + marginedHeight); 38 | 39 | AddTriAndCircum(in na_points, ref na_triangles, ref na_cirumcircles, r0, r1, r2); 40 | } 41 | 42 | /// Remove all triangles associated with the super-triangle. 43 | private static void RemoveSuperTriangle(in int pointCount, ref NativeList na_triangles) 44 | { 45 | for (int p=pointCount-3; p < pointCount; p++) 46 | { 47 | int triangleCount = na_triangles.Length/3; 48 | int removeCount = 0; 49 | for (int t=0; t < triangleCount; t++) 50 | { 51 | int idx = t - removeCount; 52 | int t0, t1, t2; 53 | GetTriangleIndices(in na_triangles, idx, out t0, out t1, out t2); 54 | 55 | if (t0 == p || t1 == p || t2 == p) 56 | { 57 | RemoveTriangle(ref na_triangles, idx); 58 | removeCount++; 59 | } 60 | } 61 | } 62 | } 63 | 64 | /// Create new triangles out of a new point by connecting each new edges to it. 65 | /// index of the point 66 | /// point location 67 | private static void CreateTrianglesForNewPoint( 68 | in int pointIdx, in float2 point, 69 | in NativeList na_edges, in NativeArray na_points, 70 | ref NativeList na_triangles, ref NativeList na_cirumcircles 71 | ) 72 | { 73 | int edgeCount = na_edges.Length; 74 | for (int e=0; e < edgeCount; e++) 75 | { 76 | Edge edge = na_edges[e]; 77 | if (edge.e0 == pointIdx || edge.e1 == pointIdx) continue; 78 | float2 p0 = na_points[edge.e0]; 79 | float2 p1 = na_points[edge.e1]; 80 | 81 | if (VGMath.IsClockwise(in point, in p0, in p1)) 82 | AddTriAndCircum(in na_points, ref na_triangles, ref na_cirumcircles, pointIdx, edge.e0, edge.e1); 83 | else 84 | AddTriAndCircum(in na_points, ref na_triangles, ref na_cirumcircles, pointIdx, edge.e1, edge.e0); 85 | } 86 | } 87 | 88 | /// 89 | /// Add edges obtained from a triangle that is going to be removed 90 | /// because its circumcircle contains a point. 91 | /// 92 | /// a list of all added edges 93 | /// 94 | /// a list of indices of duplicated edges to be removed 95 | /// 96 | private static void AddEdgesOfRemovedTriangle( 97 | in Edge edge, ref NativeList na_edges, 98 | ref NativeList na_blackListedEdges 99 | ) 100 | { 101 | if (na_edges.Contains(edge)) 102 | { 103 | int edgeIdx = na_edges.IndexOf(edge); 104 | if (!na_blackListedEdges.Contains(edgeIdx)) 105 | na_blackListedEdges.Add(edgeIdx); 106 | } else na_edges.Add(edge); 107 | } 108 | 109 | /// Remove black listed triangles. 110 | /// black listed triangle indices in ascending order 111 | private static void RemoveBlacklistedTriangles( 112 | in NativeList na_blackListedTris, ref NativeList na_triangles 113 | ) 114 | { 115 | int blacklistedTriCount = na_blackListedTris.Length; 116 | int removeCount = 0; 117 | 118 | for (int t=0; t < blacklistedTriCount; t++) 119 | RemoveTriangle(ref na_triangles, na_blackListedTris[t]-removeCount++); 120 | } 121 | 122 | /// Remove black listed edges. 123 | /// black listed edge indices in ascending order 124 | private static void RemoveBlacklistedEdges( 125 | in NativeList na_blackListedEdges, ref NativeList na_edges 126 | ) 127 | { 128 | int blackListedEdgeCount = na_blackListedEdges.Length; 129 | int removeCount = 0; 130 | 131 | for (int b=0; b < blackListedEdgeCount; b++) 132 | na_edges.RemoveAt(na_blackListedEdges[b]-removeCount++); 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /Runtime/CDT/CDT.TriangulationUtil.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1e6c3fc851b45b6458b4081d474c02de 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CDT/CDT.Util.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Unity.Mathematics; 3 | using Unity.Collections; 4 | 5 | namespace Voxell.GPUVectorGraphics 6 | { 7 | public partial class CDT 8 | { 9 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 10 | private static void GetTriangleIndices( 11 | in NativeList na_triangles, 12 | int idx, out int t0, out int t1, out int t2 13 | ) 14 | { 15 | int tIdx = idx*3; 16 | t0 = na_triangles[tIdx]; 17 | t1 = na_triangles[tIdx + 1]; 18 | t2 = na_triangles[tIdx + 2]; 19 | } 20 | 21 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 22 | private static void AddTriangle(ref NativeList na_triangles, int t0, int t1, int t2) 23 | { 24 | na_triangles.Add(t0); 25 | na_triangles.Add(t1); 26 | na_triangles.Add(t2); 27 | } 28 | 29 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 30 | private static void RemoveTriangle(ref NativeList na_triangles, int idx) 31 | { 32 | int tIdx = idx*3; 33 | na_triangles.RemoveRange(tIdx, 3); 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | private static void AddTriAndCircum( 38 | in NativeArray na_points, ref NativeList na_triangles, 39 | ref NativeList na_cirumcircles, 40 | int t0, int t1, int t2 41 | ) 42 | { 43 | AddTriangle(ref na_triangles, t0, t1, t2); 44 | float2 p0 = na_points[t0]; 45 | float2 p1 = na_points[t1]; 46 | float2 p2 = na_points[t2]; 47 | na_cirumcircles.Add(new Cirumcircle(p0, p1, p2)); 48 | } 49 | 50 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 51 | private static void RemoveTriAndCircum( 52 | ref NativeList na_cirumcircles, ref NativeList na_triangles, int idx 53 | ) 54 | { 55 | RemoveTriangle(ref na_triangles, idx); 56 | na_cirumcircles.RemoveAt(idx); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Runtime/CDT/CDT.Util.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1bf56708509871c4fa99cc92ad2e5fe0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CDT/CDT.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | using Unity.Collections; 3 | using Unity.Jobs; 4 | 5 | namespace Voxell.GPUVectorGraphics 6 | { 7 | public static partial class CDT 8 | { 9 | private const float MARGIN = 1.0f; 10 | 11 | /// Constraint delaunay triangulation based on a contour. 12 | /// minimum point of the point set 13 | /// maximum point of the point set 14 | /// points to be triangulated 15 | /// contour defining the polygon boundary 16 | /// a copy of the input point array 17 | /// output of the final triangle list 18 | /// a copy of the input contour array 19 | /// 20 | public static JobHandle ConstraintTriangulate( 21 | float2 minRect, float2 maxRect, in float2[] points, in ContourPoint[] contours, 22 | out NativeArray na_points, out NativeList na_triangles, 23 | out NativeArray na_contours 24 | ) 25 | { 26 | na_contours = new NativeArray(contours, Allocator.TempJob); 27 | JobHandle jobHandle = Triangulate(minRect, maxRect, in points, out na_points, out na_triangles); 28 | ConstrainJob job_constrain = new ConstrainJob(ref na_contours, ref na_points, ref na_triangles); 29 | return job_constrain.Schedule(jobHandle); 30 | } 31 | 32 | /// Performs a delaunay triangulation on a set of points. 33 | /// minimum point of the point set 34 | /// maximum point of the point set 35 | /// points to be triangulated 36 | /// a copy of the input point array 37 | /// output of the final triangle list 38 | /// A JobHandle the is being scheduled for delaunay triangulation. 39 | public static JobHandle Triangulate( 40 | float2 minRect, float2 maxRect, in float2[] points, 41 | out NativeArray na_points, out NativeList na_triangles 42 | ) 43 | { 44 | // last 3 points are for the super-triangle (will be used throughout the CDT process too) 45 | na_points = new NativeArray(points.Length + 3, Allocator.TempJob); 46 | na_triangles = new NativeList(Allocator.TempJob); 47 | 48 | NativeSlice na_points_slice = na_points.Slice(0, points.Length); 49 | na_points_slice.CopyFrom(points); 50 | 51 | TriangulateJob job_triangulate = new TriangulateJob( 52 | minRect, maxRect, ref na_points, ref na_triangles 53 | ); 54 | return job_triangulate.Schedule(); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Runtime/CDT/CDT.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e524ea11db66751449adb2d2816022a7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CubicBezier.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d720627e5a79e7a489db23b1818e84b1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/CubicBezier/CubicBezier.CurveType.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | 3 | namespace Voxell.GPUVectorGraphics 4 | { 5 | public static partial class CubicBezier 6 | { 7 | internal static readonly float SQRT3 = math.sqrt(3.0f); 8 | internal static readonly float DET_SQRT3 = 1/SQRT3; 9 | 10 | internal const float ONE_THIRD = 1.0f/3.0f; 11 | internal const float TWO_THIRDS = 2.0f/3.0f; 12 | 13 | internal enum CurveType 14 | { 15 | UNKNOWN = 0, 16 | SERPENTINE = 1, 17 | LOOP = 2, 18 | CUSP = 3, 19 | QUADRATIC = 4, 20 | LINE = 5 21 | } 22 | 23 | internal static CurveType ClassifyCurve( 24 | float2 p0, float2 p1, float2 p2, float2 p3, 25 | out float d0, out float d1, out float d2, out float d3 26 | ) 27 | { 28 | float3 b0 = new float3(p0, 1.0f); 29 | float3 b1 = new float3(p1, 1.0f); 30 | float3 b2 = new float3(p2, 1.0f); 31 | float3 b3 = new float3(p3, 1.0f); 32 | 33 | float a1 = math.dot(b0, math.cross(b3, b2)); 34 | float a2 = math.dot(b1, math.cross(b0, b3)); 35 | float a3 = math.dot(b2, math.cross(b1, b0)); 36 | 37 | d0 = 0.0f; 38 | d1 = a1 - 2.0f * a2 + 3.0f * a3; 39 | d2 = -a2 + 3.0f * a3; 40 | d3 = 3.0f * a3; 41 | 42 | float D = 3.0f * d2 * d2 - 4.0f * d1 * d3; 43 | float disc = d1 * d1 * D; 44 | 45 | if (disc == 0.0f) 46 | { 47 | if (d1 == 0.0f && d2 == 0.0f) 48 | { 49 | if (d3 == 0.0f) return CurveType.LINE; 50 | return CurveType.QUADRATIC; 51 | } 52 | 53 | if (d1 != 0.0f) return CurveType.CUSP; 54 | if (D < 0.0f) return CurveType.LOOP; 55 | 56 | return CurveType.SERPENTINE; 57 | } 58 | 59 | if (disc > 0.0f) return CurveType.SERPENTINE; 60 | else return CurveType.LOOP; 61 | } 62 | 63 | public static float3x4 Serpentine(float d1, float d2, float d3, ref bool flip) 64 | { 65 | float t1 = math.sqrt(9.0f * d2 * d2 - 12 * d1 * d3); 66 | float ls = 3.0f * d2 - t1; 67 | float lt = 6.0f * d1; 68 | float ms = 3.0f * d2 + t1; 69 | float mt = lt; 70 | float ltMinusLs = lt - ls; 71 | float mtMinusMs = mt - ms; 72 | 73 | float3x4 coords = new float3x4(); 74 | coords.c0.x = ls * ms; 75 | coords.c0.y = ls * ls * ls; 76 | coords.c0.z = ms * ms * ms; 77 | 78 | coords.c1.x = ONE_THIRD * (3.0f * ls * ms - ls * mt - lt * ms); 79 | coords.c1.y = ls * ls * (ls - lt); 80 | coords.c1.z = ms * ms * (ms - mt); 81 | 82 | coords.c2.x = ONE_THIRD * (lt * (mt - 2.0f * ms) + ls * (3.0f * ms - 2.0f * mt)); 83 | coords.c2.y = ltMinusLs * ltMinusLs * ls; 84 | coords.c2.z = mtMinusMs * mtMinusMs * ms; 85 | 86 | coords.c3.x = ltMinusLs * mtMinusMs; 87 | coords.c3.y = -(ltMinusLs * ltMinusLs * ltMinusLs); 88 | coords.c3.z = -(mtMinusMs * mtMinusMs * mtMinusMs); 89 | 90 | flip = d1 < 0.0f; 91 | return coords; 92 | } 93 | 94 | public static float3x4 Loop( 95 | float d1, float d2, float d3, ref bool flip, 96 | ref int loopArtifact, ref float splitParam, int recursiveType 97 | ) 98 | { 99 | float t1 = math.sqrt(4.0f * d1 * d3 - 3.0f * d2 * d2); 100 | float ls = d2 - t1; 101 | float lt = 2.0f * d1; 102 | float ms = d2 + t1; 103 | float mt = lt; 104 | 105 | // Figure out whether there is a rendering artifact requiring 106 | // the curve to be subdivided by the caller. 107 | float ql = ls / lt; 108 | float qm = ms / mt; 109 | if (0.0f < ql && ql < 1.0f) 110 | { 111 | loopArtifact = 1; 112 | splitParam = ql; 113 | } 114 | 115 | if (0.0f < qm && qm < 1.0f) 116 | { 117 | loopArtifact = 2; 118 | splitParam = qm; 119 | } 120 | 121 | float ltMinusLs = lt - ls; 122 | float mtMinusMs = mt - ms; 123 | 124 | float3x4 coords = new float3x4(); 125 | coords.c0.x = ls * ms; 126 | coords.c0.y = ls * ls * ms; 127 | coords.c0.z = ls * ms * ms; 128 | 129 | coords.c1.x = ONE_THIRD * (-ls * mt - lt * ms + 3.0f * ls * ms); 130 | coords.c1.y = -ONE_THIRD * ls * (ls * (mt - 3.0f * ms) + 2.0f * lt * ms); 131 | coords.c1.z = -ONE_THIRD * ms * (ls * (2.0f * mt - 3.0f * ms) + lt * ms); 132 | 133 | coords.c2.x = ONE_THIRD * (lt * (mt - 2.0f * ms) + ls * (3.0f * ms - 2.0f * mt)); 134 | coords.c2.y = ONE_THIRD * (lt - ls) * (ls * (2.0f * mt - 3.0f * ms) + lt * ms); 135 | coords.c2.z = ONE_THIRD * (mt - ms) * (ls * (mt - 3.0f * ms) + 2.0f * lt * ms); 136 | 137 | coords.c3.x = ltMinusLs * mtMinusMs; 138 | coords.c3.y = -(ltMinusLs * ltMinusLs) * mtMinusMs; 139 | coords.c3.z = -ltMinusLs * mtMinusMs * mtMinusMs; 140 | 141 | if (recursiveType == -1) 142 | flip = (d1 > 0.0f && coords.c0.x < 0.0f) || (d1 < 0.0f && coords.c0.x > 0.0f); 143 | return coords; 144 | } 145 | 146 | public static float3x4 Cusp(float d1, float d2, float d3) 147 | { 148 | float ls = d3; 149 | float lt = 3.0f * d2; 150 | float lsMinusLt = ls - lt; 151 | 152 | float3x4 coords = new float3x4(); 153 | coords.c0.x = ls; 154 | coords.c0.y = ls * ls * ls; 155 | coords.c0.z = 1.0f; 156 | 157 | coords.c1.x = ls - TWO_THIRDS * lt; 158 | coords.c1.y = ls * ls * lsMinusLt; 159 | coords.c1.z = 1.0f; 160 | 161 | coords.c2.x = ls - TWO_THIRDS * lt; 162 | coords.c2.y = lsMinusLt * lsMinusLt * ls; 163 | coords.c2.z = 1.0f; 164 | 165 | coords.c3.x = lsMinusLt; 166 | coords.c3.y = lsMinusLt * lsMinusLt * lsMinusLt; 167 | coords.c3.z = 1.0f; 168 | 169 | return coords; 170 | } 171 | 172 | public static float3x4 Quadratic(float d3, ref bool flip) 173 | { 174 | float3x4 coords = new float3x4( 175 | 0.0f, 0.0f, 0.0f, 176 | ONE_THIRD, 0.0f, ONE_THIRD, 177 | TWO_THIRDS, ONE_THIRD, TWO_THIRDS, 178 | 1.0f, 1.0f, 1.0f 179 | ); 180 | 181 | flip = d3 < 0.0f; 182 | return coords; 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /Runtime/CubicBezier/CubicBezier.CurveType.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bd1759239d76c1049bbfd046ea357200 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CubicBezier/CubicBezier.Geometry.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Unity.Mathematics; 3 | using Unity.Collections; 4 | 5 | namespace Voxell.GPUVectorGraphics 6 | { 7 | public static partial class CubicBezier 8 | { 9 | public static void ComputeCubic( 10 | float2 p0, float2 p1, float2 p2, float2 p3, 11 | ref int vertexStart, ref NativeSlice vertexSlice, 12 | ref int coordsStart, ref NativeSlice coordsSlice, 13 | int recursiveType = -1 14 | ) 15 | { 16 | float d0, d1, d2, d3; 17 | float3x4 coords = new float3x4(); 18 | bool flip = false; 19 | // artifact on loop 20 | int loopArtifact = -1; 21 | float splitParam = 0.0f; 22 | CurveType curveType = ClassifyCurve(p0, p1, p2, p3, out d0, out d1, out d2, out d3); 23 | 24 | switch (curveType) 25 | { 26 | case CurveType.SERPENTINE: 27 | coords = Serpentine(d1, d2, d3, ref flip); 28 | break; 29 | 30 | case CurveType.LOOP: 31 | coords = Loop(d1, d2, d3, ref flip, ref loopArtifact, ref splitParam, recursiveType); 32 | break; 33 | 34 | case CurveType.CUSP: 35 | coords = Cusp(d1, d2, d3); 36 | break; 37 | 38 | case CurveType.QUADRATIC: 39 | coords = Quadratic(d3, ref flip); 40 | break; 41 | 42 | default: return; 43 | } 44 | 45 | // recursive computation 46 | if (loopArtifact != -1 && recursiveType == -1) 47 | { 48 | float2 p01 = (p1 - p0) * splitParam + p0; 49 | float2 p12 = (p2 - p1) * splitParam + p1; 50 | float2 p23 = (p3 - p2) * splitParam + p2; 51 | 52 | float2 p012 = (p12 - p01) * splitParam + p01; 53 | float2 p123 = (p23 - p12) * splitParam + p12; 54 | 55 | float2 p0123 = (p123 - p012) * splitParam + p012; 56 | 57 | if (loopArtifact == 1) // flip second 58 | { 59 | ComputeCubic(p0, p01, p012, p0123, ref vertexStart, ref vertexSlice, ref coordsStart, ref coordsSlice, 0); 60 | ComputeCubic(p0123, p123, p23, p3, ref vertexStart, ref vertexSlice, ref coordsStart, ref coordsSlice, 1); 61 | } else if (loopArtifact == 2) // flip first 62 | { 63 | ComputeCubic(p0, p01, p012, p0123, ref vertexStart, ref vertexSlice, ref coordsStart, ref coordsSlice, 1); 64 | ComputeCubic(p0123, p123, p23, p3, ref vertexStart, ref vertexSlice, ref coordsStart, ref coordsSlice, 0); 65 | } 66 | return; 67 | } 68 | 69 | if (recursiveType == 1) flip = !flip; 70 | if (flip) 71 | { 72 | coords[0].xy = -coords[0].xy; 73 | coords[1].xy = -coords[1].xy; 74 | coords[2].xy = -coords[2].xy; 75 | coords[3].xy = -coords[3].xy; 76 | } 77 | 78 | // triangulate 79 | Triangulate( 80 | new float2x4(p0, p1, p2, p3), coords, 81 | ref vertexStart, ref vertexSlice, 82 | ref coordsStart, ref coordsSlice 83 | ); 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /Runtime/CubicBezier/CubicBezier.Geometry.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2087e6dd157341347b88ad643983422b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/CubicBezier/CubicBezier.Triangulate.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | using Unity.Collections; 3 | 4 | namespace Voxell.GPUVectorGraphics 5 | { 6 | public static partial class CubicBezier 7 | { 8 | internal static void Triangulate( 9 | float2x4 points, float3x4 coords, 10 | ref int vertexStart, ref NativeSlice vertexSlice, 11 | ref int coordsStart, ref NativeSlice coordsSlice 12 | ) 13 | { 14 | // test for degenerate cases. 15 | for (int i=0; i < 4; i++) 16 | { 17 | for (int j=i + 1; j < 4; j++) 18 | { 19 | if (math.distance(points[i], points[j]) == 0.0f) 20 | { 21 | // Two of the points are coincident, so we can eliminate at 22 | // least one triangle. We might be able to eliminate the other 23 | // as well, but this seems sufficient to avoid degenerate triangulations. 24 | 25 | NativeArray indices = new NativeArray(3, Allocator.Temp); 26 | int index = 0; 27 | for (int k=0; k < 4; ++k) 28 | if (k != j) indices[index++] = k; 29 | 30 | CreateTriangleIndices( 31 | ref vertexStart, ref vertexSlice, 32 | ref coordsStart, ref coordsSlice, 33 | indices[0], indices[1], indices[2], 34 | points, coords 35 | ); 36 | 37 | indices.Dispose(); 38 | return; 39 | } 40 | } 41 | } 42 | 43 | // see whether any of the points are fully contained in the 44 | // triangle defined by the other three. 45 | for (int i=0; i < 4; ++i) 46 | { 47 | NativeArray indices = new NativeArray(3, Allocator.Temp); 48 | int index = 0; 49 | for (int j=0; j < 4; ++j) 50 | if (i != j) indices[index++] = j; 51 | 52 | if (VGMath.PointInTriangle(points[i], points[indices[0]], points[indices[1]], points[indices[2]])) 53 | { 54 | // produce three triangles surrounding this interior vertex. 55 | for (int j=0; j < 3; ++j) 56 | { 57 | CreateTriangleIndices( 58 | ref vertexStart, ref vertexSlice, 59 | ref coordsStart, ref coordsSlice, 60 | indices[j % 3], indices[(j + 1) % 3], i, 61 | points, coords 62 | ); 63 | } 64 | 65 | indices.Dispose(); 66 | return; 67 | } 68 | } 69 | 70 | // There are only a few permutations of the points, ignoring 71 | // rotations, which are irrelevant: 72 | 73 | // 0--3 0--2 0--3 0--1 0--2 0--1 74 | // | | | | | | | | | | | | 75 | // | | | | | | | | | | | | 76 | // 1--2 1--3 2--1 2--3 3--1 3--2 77 | 78 | // Note that three of these are reflections of each other. 79 | // Therefore there are only three possible triangulations: 80 | 81 | // 0--3 0--2 0--3 82 | // |\ | |\ | |\ | 83 | // | \| | \| | \| 84 | // 1--2 1--3 2--1 85 | 86 | // From which we can choose by seeing which of the potential 87 | // diagonals intersect. Note that we choose the shortest diagonal 88 | // to split the quad. 89 | if (VGMath.LinesIntersect(points[0], points[2], points[1], points[3])) 90 | { 91 | if (math.lengthsq(points[2] - points[0]) < math.lengthsq(points[3] - points[1])) 92 | { 93 | CreateTriangleIndices( 94 | ref vertexStart, ref vertexSlice, 95 | ref coordsStart, ref coordsSlice, 96 | 0, 1, 2, points, coords 97 | ); 98 | CreateTriangleIndices( 99 | ref vertexStart, ref vertexSlice, 100 | ref coordsStart, ref coordsSlice, 101 | 0, 2, 3, points, coords 102 | ); 103 | } else 104 | { 105 | CreateTriangleIndices( 106 | ref vertexStart, ref vertexSlice, 107 | ref coordsStart, ref coordsSlice, 108 | 0, 1, 3, points, coords 109 | ); 110 | CreateTriangleIndices( 111 | ref vertexStart, ref vertexSlice, 112 | ref coordsStart, ref coordsSlice, 113 | 1, 2, 3, points, coords 114 | ); 115 | } 116 | } else if (VGMath.LinesIntersect(points[0], points[3], points[1], points[2])) 117 | { 118 | if (math.lengthsq(points[3] - points[0]) < math.lengthsq(points[2] - points[1])) 119 | { 120 | CreateTriangleIndices( 121 | ref vertexStart, ref vertexSlice, 122 | ref coordsStart, ref coordsSlice, 123 | 0, 1, 3, points, coords 124 | ); 125 | CreateTriangleIndices( 126 | ref vertexStart, ref vertexSlice, 127 | ref coordsStart, ref coordsSlice, 128 | 0, 3, 2, points, coords 129 | ); 130 | } else 131 | { 132 | CreateTriangleIndices( 133 | ref vertexStart, ref vertexSlice, 134 | ref coordsStart, ref coordsSlice, 135 | 0, 1, 2, points, coords 136 | ); 137 | CreateTriangleIndices( 138 | ref vertexStart, ref vertexSlice, 139 | ref coordsStart, ref coordsSlice, 140 | 2, 1, 3, points, coords 141 | ); 142 | } 143 | } else 144 | { 145 | // Lines (0->1), (2->3) intersect -- or should, modulo numerical 146 | // precision issues 147 | if (math.lengthsq(points[1] - points[0]) < math.lengthsq(points[3] - points[2])) 148 | { 149 | CreateTriangleIndices( 150 | ref vertexStart, ref vertexSlice, 151 | ref coordsStart, ref coordsSlice, 152 | 0, 2, 1, points, coords 153 | ); 154 | CreateTriangleIndices( 155 | ref vertexStart, ref vertexSlice, 156 | ref coordsStart, ref coordsSlice, 157 | 0, 1, 3, points, coords 158 | ); 159 | } else 160 | { 161 | CreateTriangleIndices( 162 | ref vertexStart, ref vertexSlice, 163 | ref coordsStart, ref coordsSlice, 164 | 0, 2, 3, points, coords 165 | ); 166 | CreateTriangleIndices( 167 | ref vertexStart, ref vertexSlice, 168 | ref coordsStart, ref coordsSlice, 169 | 3, 2, 1, points, coords 170 | ); 171 | } 172 | } 173 | } 174 | 175 | private static void CreateTriangleIndices( 176 | ref int vertexStart, ref NativeSlice vertexSlice, 177 | ref int coordsStart, ref NativeSlice coordsSlice, 178 | int idx0, int idx1, int idx2, float2x4 points, float3x4 coords 179 | ) 180 | { 181 | vertexSlice[vertexStart] = points[idx0]; 182 | coordsSlice[vertexStart++] = coords[idx0]; 183 | 184 | vertexSlice[vertexStart] = points[idx1]; 185 | coordsSlice[vertexStart++] = coords[idx1]; 186 | 187 | vertexSlice[vertexStart] = points[idx2]; 188 | coordsSlice[vertexStart++] = coords[idx2]; 189 | } 190 | } 191 | } -------------------------------------------------------------------------------- /Runtime/CubicBezier/CubicBezier.Triangulate.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b7056eed21ef0c8409c1cac2840735cc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 618e75c1f854d6f439e60d8e4fc1af82 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Font/FontCurve.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Unity.Mathematics; 5 | using Unity.Collections; 6 | 7 | namespace Voxell.GPUVectorGraphics.Font 8 | { 9 | public class FontCurve : ScriptableObject 10 | { 11 | public Glyph[] Glyphs => _glyphs; 12 | public int[] CharCodes => _charCodes; 13 | public int[] GlyphIndices => _glyphIndices; 14 | 15 | /// Bezier contour for each character. 16 | [SerializeField, NonReorderable] private Glyph[] _glyphs; 17 | 18 | /// Supported character codes. 19 | [SerializeField, NonReorderable] private int[] _charCodes; 20 | /// Glyph indices of the corresponding character codes. 21 | [SerializeField, NonReorderable] private int[] _glyphIndices; 22 | /// Meshes of the corresponding character glyph indices. 23 | [SerializeField, NonReorderable] private Mesh[] _meshes; 24 | 25 | public void Initialize(Glyph[] glyphs, int[] charCodes, int[] glyphIndices, Mesh[] meshes) 26 | { 27 | _glyphs = glyphs; 28 | _charCodes = charCodes; 29 | _glyphIndices = glyphIndices; 30 | _meshes = meshes; 31 | } 32 | 33 | /// Search for the glyph index of a certain character through binary search. 34 | /// 0 if character does not exsists, else index of the character's glyph. 35 | public int SearchGlyhIndex(char character) 36 | { 37 | int __len = _charCodes.Length; 38 | int __first = 0; 39 | int __half; 40 | int __middle; 41 | 42 | // binary search 43 | while (__len > 0) 44 | { 45 | __half = __len >> 1; 46 | __middle = __first + __half; 47 | 48 | int midCode = _charCodes[__middle]; 49 | if (character == midCode) return _glyphIndices[__middle]; 50 | 51 | // search first half if it is lower than the mid point 52 | if (character < midCode) 53 | __len = __half; 54 | // search second half if it is higher than the mid point 55 | else 56 | { 57 | __first = __middle + 1; 58 | __len = __len - __half - 1; 59 | } 60 | } 61 | 62 | // index of "square" glyph (a glyph repersenting that the character is not supported) 63 | return 0; 64 | } 65 | 66 | /// Get character glyph through binary search. 67 | /// 68 | /// First glyph if character is not supported, 69 | /// else the glyph representing the shape of the character 70 | /// 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | public Glyph GetCharacterGlyph(char character) => _glyphs[SearchGlyhIndex(character)]; 73 | 74 | /// Get character mesh through binary search. 75 | /// 76 | /// First mesh if character is not supported, 77 | /// else the mesh representing the shape of the character 78 | /// 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public Mesh GetCharacterMesh(char character) => _meshes[SearchGlyhIndex(character)]; 81 | 82 | /// Convert glyphs into points and contours. 83 | public static void ExtractGlyphData( 84 | in Glyph glyph, out float2[] points, out CDT.ContourPoint[] contours 85 | ) 86 | { 87 | int contourCount = glyph.contours.Length; 88 | List pointList = new List(); 89 | List contourList = new List(); 90 | 91 | int contourStart = 0; 92 | for (int c=0; c < contourCount; c++) 93 | { 94 | QuadraticContour glyphContour = glyph.contours[c]; 95 | int segmentCount = glyphContour.segments.Length; 96 | 97 | for (int s=0; s < segmentCount; s++) 98 | { 99 | pointList.Add(glyphContour.segments[s].p0); 100 | contourList.Add(new CDT.ContourPoint(contourStart+s, c)); 101 | } 102 | contourList.Add(new CDT.ContourPoint(contourStart, c)); 103 | contourStart += segmentCount; 104 | } 105 | 106 | points = pointList.ToArray(); 107 | contours = contourList.ToArray(); 108 | } 109 | 110 | public static Vector3[] PointsToVertices(in NativeArray points) 111 | { 112 | Vector3[] vertices = new Vector3[points.Length]; 113 | for (int p=0; p < points.Length; p++) 114 | { 115 | float2 point = points[p]; 116 | vertices[p] = new Vector3(point.x, point.y, 0.0f); 117 | } 118 | 119 | return vertices; 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /Runtime/Font/FontCurve.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d1f28eba00cd7a4dbce389f3b22feb8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {fileID: 2800000, guid: f04b41271e371d047854280baa7d3658, type: 3} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Glyph.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | 3 | namespace Voxell.GPUVectorGraphics.Font 4 | { 5 | [System.Serializable] 6 | public struct Glyph 7 | { 8 | [System.Serializable] 9 | public struct CompositeReference 10 | { 11 | /// The offset for the referenced glyph. 12 | public float2 offset; 13 | 14 | /// The X axis for the referenced glyph. 15 | public float2 xAxis; 16 | 17 | /// The Y axis for the referenced glyph. 18 | public float2 yAxis; 19 | 20 | /// The index of the glyph being referenced. 21 | public int glyphRef; 22 | } 23 | 24 | /// All contours in the glyph. 25 | public QuadraticContour[] contours; 26 | 27 | /// Composition of referenes of other glyphs. 28 | public CompositeReference[] compositeReferences; 29 | 30 | /// Determines if this glyph is made up of other glyph or a glyph on its own. 31 | public bool isComplex; 32 | 33 | /// Bottom left of the glyph's bounding box. 34 | public float2 minRect; 35 | /// Top right of the glyph's bounding box. 36 | public float2 maxRect; 37 | } 38 | } -------------------------------------------------------------------------------- /Runtime/Font/Glyph.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 68f867a8c7225ec46aac96abc5290e1f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Reader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b5ab9495e477c5145b04e7586cbbbac0 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Font/Reader/FontReader.cs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Pixel Precision LLC 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | using System.IO; 24 | 25 | namespace Voxell.GPUVectorGraphics.Font 26 | { 27 | /// 28 | /// Utility class used by Berny to read a TrueType Font file and other 29 | /// related files and data streams. 30 | /// 31 | public abstract class FontReader 32 | { 33 | /// Returning access and resources back to the system. 34 | /// True if the reader was successfully closed. Else, false. 35 | /// May not be relevant on all platforms and implementations of TTFReader, but 36 | /// best practice is to call Close() at the proper time, regardless of the implementation 37 | /// or usecase. The only exception is explicit use to TTFReaderBytes. 38 | public abstract bool Close(); 39 | 40 | /// 41 | /// Read a 1 byte un signed integer from the read position. 42 | /// 43 | /// The read integer value. 44 | public abstract sbyte ReadInt8(); 45 | 46 | /// 47 | /// Read a 1 byte signed integer from the read position. 48 | /// 49 | /// The read integer value. 50 | public abstract byte ReadUInt8(); 51 | 52 | /// 53 | /// Returns the read position. 54 | /// 55 | /// The read position. 56 | public abstract long GetPosition(); 57 | 58 | /// 59 | /// Set the read position of the reader. 60 | /// 61 | /// The new read position. 62 | /// If true, the read position was successfully set. Else, false. 63 | public abstract bool SetPosition(long pos, SeekOrigin seekOrigin = SeekOrigin.Begin); 64 | 65 | /// 66 | /// Read an array of bytes, starting from the read position. 67 | /// 68 | /// The number of bytes to read. 69 | /// The read bytes. 70 | public abstract byte[] ReadBytes(int length); 71 | 72 | public sbyte[] ReadSBytes(int length) 73 | { 74 | // Reading SBytes is far less-used than ReadBytes, so we just do 75 | // our own implementation instead of forcing the implemenenters 76 | // to make their own. 77 | sbyte [] ret = new sbyte[length]; 78 | 79 | for(int i = 0; i < length; ++i) 80 | ret[i] = this.ReadInt8(); 81 | 82 | return ret; 83 | } 84 | 85 | /// 86 | /// If true, the read position is past the readable area of the data stream. 87 | /// Else, false. 88 | /// 89 | /// If false, there is still data that can be read from where the read position is. 90 | public abstract bool AtEnd(); 91 | 92 | /// 93 | /// Read a signed 16 int from the current read position. 94 | /// 95 | /// The read integer value. 96 | /// Current implementation is hard coded for little endian platforms. 97 | public ushort ReadUInt16() 98 | { 99 | return (ushort)(this.ReadUInt8() << 8 | this.ReadUInt8()); 100 | } 101 | 102 | /// 103 | /// Read a 24 bit unsigned value from the current read position. 104 | /// 105 | /// The read integer value. 106 | /// Current implementation is hard coded for little endian platforms. 107 | public uint ReadUInt24() 108 | { 109 | return (uint)((this.ReadUInt8() << 16) | (this.ReadUInt8() << 8) | (this.ReadUInt8() << 0)); 110 | } 111 | 112 | /// 113 | /// Read a 32 bit unsigned value from the current read position. 114 | /// 115 | /// Current implementation is hard coded for little endian platforms. 116 | public uint ReadUInt32() 117 | { 118 | return (uint)((this.ReadUInt8() << 24) | (this.ReadUInt8() << 16) | (this.ReadUInt8() << 8) | (this.ReadUInt8() << 0)); 119 | } 120 | 121 | public long ReadUInt64() 122 | { 123 | return ( 124 | ((long)this.ReadUInt8() << 56) | 125 | ((long)this.ReadUInt8() << 48) | 126 | ((long)this.ReadUInt8() << 40) | 127 | ((long)this.ReadUInt8() << 32) | 128 | ((long)this.ReadUInt8() << 24) | 129 | ((long)this.ReadUInt8() << 16) | 130 | ((long)this.ReadUInt8() << 8) | 131 | ((long)this.ReadUInt8() << 0)); 132 | } 133 | 134 | /// 135 | /// Read a 16 bit signed value from the current read position. 136 | /// 137 | /// 138 | /// Current implementation is hard coded for little endian platforms. 139 | public short ReadInt16() 140 | { 141 | return (short)(this.ReadUInt8() << 8 | this.ReadUInt8()); 142 | } 143 | 144 | /// 145 | /// Read a 32 bit signed value from the current read position. 146 | /// 147 | /// 148 | /// Current implementation is hard coded for little endian platforms. 149 | public int ReadInt32() 150 | { 151 | return (int)((this.ReadUInt8() << 24) | (this.ReadUInt8() << 16) | (this.ReadUInt8() << 8) | (this.ReadUInt8() << 0)); 152 | } 153 | 154 | /// 155 | /// Read a TTF FWord from the current read position. 156 | /// 157 | /// The value read from the data stream. 158 | public short ReadFWord() 159 | { 160 | return this.ReadInt16(); 161 | } 162 | 163 | /// 164 | /// Read a TTF UFWord from the current read position. 165 | /// 166 | /// The value read from the data stream. 167 | public ushort ReadUFWord() 168 | { 169 | return this.ReadUInt16(); 170 | } 171 | 172 | /// 173 | /// Read a TTF Offset16 value from the current read position. 174 | /// 175 | /// The value read from the data stream. 176 | public ushort ReadOffset16() 177 | { 178 | return this.ReadUInt16(); 179 | } 180 | 181 | /// 182 | /// Read a TTF Offset32 value from the current read position. 183 | /// 184 | /// The value read from the data stream. 185 | public int ReadOffset32() 186 | { 187 | return this.ReadOffset32(); 188 | } 189 | 190 | /// 191 | /// Reads a TTF FDot14 value from the current read position. 192 | /// 193 | /// The number value read from the data stream and converted to a float. 194 | public float ReadFDot14() 195 | { 196 | return (float)this.ReadInt16() / (float)(1 << 14); 197 | } 198 | 199 | /// 200 | /// Reads a TTF Fixed value from the current read position. 201 | /// 202 | /// the number value read from the data stream and converted to a float. 203 | public float ReadFixed() 204 | { 205 | return (float)this.ReadInt32() / (float)(1 << 16); 206 | } 207 | 208 | /// 209 | /// Read an ASCII string of a known length from file. 210 | /// 211 | /// The length of the string. 212 | /// The string read from the data stream. 213 | public string ReadString(int length) 214 | { 215 | byte[] rbStr = this.ReadBytes(length); 216 | return System.Text.ASCIIEncoding.ASCII.GetString(rbStr); 217 | } 218 | 219 | /// 220 | /// Read a record string from file. A record string is a pascal 221 | /// string with a 2 byte length. 222 | /// 223 | /// The string read from the data stream. 224 | public string ReadNameRecord() 225 | { 226 | ushort len = this.ReadUInt16(); 227 | return this.ReadString(len); 228 | } 229 | 230 | /// 231 | /// Read a string from file in pascal format - where the first byte 232 | /// defines the length of the string, directly followed by the ASCII 233 | /// data. 234 | /// 235 | /// The string read from the datastream. 236 | public string ReadPascalString() 237 | { 238 | byte c = this.ReadUInt8(); 239 | return this.ReadString(c); 240 | } 241 | 242 | // A strategy used in reading is to create file member variables 243 | // of the correct byte width and "signed-ness", and just use 244 | // ReadInt(out * i) and let the overloading resolve figure out 245 | // the proper Read*() function. 246 | 247 | /// 248 | /// Overload of ReadInt() for 1 byte unsigned int. 249 | /// 250 | /// The output variable. 251 | public void ReadInt(out sbyte i) 252 | { 253 | i = this.ReadInt8(); 254 | } 255 | 256 | /// 257 | /// Overload of ReadInt() for 1 byte signed int. 258 | /// 259 | /// The output variable. 260 | public void ReadInt(out byte i) 261 | { 262 | i = this.ReadUInt8(); 263 | } 264 | 265 | /// 266 | /// Overload of ReadInt() for 2 byte signed int. 267 | /// 268 | /// The output variable. 269 | public void ReadInt(out short i) 270 | { 271 | i = this.ReadInt16(); 272 | } 273 | 274 | /// 275 | /// Overload of ReadInt() for 2 byte unsigned int. 276 | /// 277 | /// The output variable. 278 | public void ReadInt(out ushort i) 279 | { 280 | i = this.ReadUInt16(); 281 | } 282 | 283 | /// 284 | /// Overload of ReadInt() for 4 byte signed int. 285 | /// 286 | /// The output variable. 287 | public void ReadInt(out int i) 288 | { 289 | i = this.ReadInt32(); 290 | } 291 | 292 | /// 293 | /// Overload of ReadInt() for 4 byte unsigned int. 294 | /// 295 | /// The output variable. 296 | public void ReadInt(out uint i) 297 | { 298 | i = this.ReadUInt32(); 299 | } 300 | 301 | /// 302 | /// Reads a date time from the TTF file. 303 | /// 304 | /// The datetime at the current read position. 305 | public System.DateTime ReadDate() 306 | { 307 | System.DateTime dt = new System.DateTime(1904, 1, 1); 308 | dt = dt.AddSeconds(this.ReadUInt64()); 309 | 310 | return dt; 311 | } 312 | } 313 | } -------------------------------------------------------------------------------- /Runtime/Font/Reader/FontReader.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3d55171f2b5c96a4896e5d563ace868b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Reader/FontReaderFile.cs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Pixel Precision LLC 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | using System.IO; 24 | 25 | namespace Voxell.GPUVectorGraphics.Font 26 | { 27 | /// 28 | /// An implementation of the TTFReader that pulls data from a file. 29 | /// 30 | public class FontReaderFile : FontReader 31 | { 32 | /// The file reader. 33 | BinaryReader reader = null; 34 | 35 | /// The file stream used by the binary reader. 36 | FileStream filestream = null; 37 | 38 | /// File path constructor. 39 | /// The file path to open. 40 | public FontReaderFile(string path) 41 | { 42 | if (this.Open(path) == false) 43 | throw new System.Exception("Could not open file"); 44 | } 45 | 46 | /// Opens a file. 47 | /// The file path to open. 48 | /// If true, the file was successfully opened. Else, false. 49 | public bool Open(string path) 50 | { 51 | this.Close(); 52 | 53 | this.filestream = System.IO.File.Open(path, System.IO.FileMode.Open); 54 | reader = new System.IO.BinaryReader(this.filestream); 55 | return true; 56 | } 57 | 58 | public override bool Close() 59 | { 60 | if (this.filestream != null) 61 | { 62 | this.filestream.Close(); 63 | this.filestream = null; 64 | this.reader = null; 65 | 66 | return true; 67 | } 68 | return false; 69 | } 70 | 71 | public override bool AtEnd() => this.filestream.Position < this.filestream.Length; 72 | 73 | public override sbyte ReadInt8() => this.reader.ReadSByte(); 74 | 75 | public override byte ReadUInt8() => this.reader.ReadByte(); 76 | 77 | public override long GetPosition() => this.filestream.Position; 78 | 79 | public override bool SetPosition(long pos, SeekOrigin seekOrigin = SeekOrigin.Begin) 80 | { 81 | if (this.filestream == null) return false; 82 | return this.filestream.Seek(pos, seekOrigin) == pos; 83 | } 84 | 85 | public override byte [] ReadBytes(int length) => this.reader.ReadBytes(length); 86 | } 87 | } -------------------------------------------------------------------------------- /Runtime/Font/Reader/FontReaderFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b679b744158d7894d8ca27c68580a66c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Tables.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a1282eea054fbbe4d83f5fdb47cdb7ef 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Font/Tables/CMap.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3d165bd8a8f261a49b5578629b889b9e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Tables/Glyf.cs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Pixel Precision LLC 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | using System.Collections.Generic; 24 | using UnityEngine; 25 | 26 | namespace Voxell.GPUVectorGraphics.Font 27 | { 28 | public struct Glyf 29 | { 30 | public struct CompositeEntry 31 | { 32 | public ushort flags; // composite flag 33 | public ushort glyphIndex; // glyph index of component 34 | public int argument1; // x-offset for component or point number; type depends on bits 0 and 1 in component flags 35 | public int argument2; // y-offset for component or point number; type depends on bits 0 and 1 in component flags 36 | 37 | public float scale; 38 | public float xscale; 39 | public float scale01; 40 | public float scale10; 41 | public float yscale; 42 | 43 | public bool Read(FontReader r) 44 | { 45 | // Set defaults in case we don't read them 46 | // because it's filtered out from the flags. 47 | this.argument1 = 0; 48 | this.argument2 = 0; 49 | this.scale = 1.0f; 50 | this.xscale = 1.0f; 51 | this.yscale = 1.0f; 52 | this.scale01 = 0.0f; 53 | this.scale10 = 0.0f; 54 | 55 | r.ReadInt(out this.flags); 56 | r.ReadInt(out this.glyphIndex); 57 | 58 | if ((this.flags & ARG_1_AND_2_ARE_WORDS) != 0) 59 | { 60 | short a1 = r.ReadInt16(); 61 | short a2 = r.ReadInt16(); 62 | 63 | if ((this.flags & ARGS_ARE_XY_VALUES) != 0) 64 | { 65 | this.argument1 = a1; 66 | this.argument2 = a2; 67 | } 68 | else 69 | { 70 | this.argument1 = (ushort)a1; 71 | this.argument2 = (ushort)a2; 72 | } 73 | } 74 | else 75 | { 76 | sbyte a1 = r.ReadInt8(); 77 | sbyte a2 = r.ReadInt8(); 78 | 79 | if ((this.flags & ARGS_ARE_XY_VALUES) != 0) 80 | { 81 | this.argument1 = a1; 82 | this.argument2 = a2; 83 | } 84 | else 85 | { 86 | this.argument1 = (byte)a1; 87 | this.argument2 = (byte)a2; 88 | } 89 | } 90 | if ((this.flags & WE_HAVE_A_SCALE) != 0) 91 | { 92 | this.scale = r.ReadFDot14(); 93 | } 94 | else if ((this.flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0) 95 | { 96 | this.xscale = r.ReadFDot14(); // Format 2.14 97 | this.yscale = r.ReadFDot14(); // Format 2.14 98 | } 99 | else if ((this.flags & WE_HAVE_A_TWO_BY_TWO) != 0) 100 | { 101 | this.xscale = r.ReadFDot14(); // Format 2.14 102 | this.scale01 = r.ReadFDot14(); // Format 2.14 103 | this.scale10 = r.ReadFDot14(); // Format 2.14 104 | this.yscale = r.ReadFDot14(); // Format 2.14 105 | } 106 | 107 | return (this.flags & MORE_COMPONENTS) != 0; 108 | } 109 | } 110 | 111 | // Simple Glyph Flags 112 | public const byte ON_CURVE_POINT = 0x01; // Bit 0: If set, the point is on the curve; otherwise, it is off the curve. 113 | public const byte X_SHORT_VECTOR = 0x02; // Bit 1: If set, the corresponding x-coordinate is 1 byte long. If not set, it is two bytes long. For the sign of this value, see the description of the X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR flag. 114 | public const byte Y_SHORT_VECTOR = 0x04; // Bit 2: If set, the corresponding y-coordinate is 1 byte long. If not set, it is two bytes long. For the sign of this value, see the description of the Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR flag. 115 | public const byte REPEAT_FLAG = 0x08; // Bit 3: If set, the next byte (read as unsigned) specifies the number of additional times this flag byte is to be repeated in the logical flags array — that is, the number of additional logical flag entries inserted after this entry. (In the expanded logical array, this bit is ignored.) In this way, the number of flags listed can be smaller than the number of points in the glyph description. 116 | public const byte X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 0x10; // Bit 4: This flag has two meanings, depending on how the X_SHORT_VECTOR flag is set. If X_SHORT_VECTOR is set, this bit describes the sign of the value, with 1 equaling positive and 0 negative.If X_SHORT_VECTOR is not set and this bit is set, then the current x-coordinate is the same as the previous x-coordinate.If X_SHORT_VECTOR is not set and this bit is also not set, the current x - coordinate is a signed 16 - bit delta vector. 117 | public const byte Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 0x20; // Bit 5: This flag has two meanings, depending on how the Y_SHORT_VECTOR flag is set.If Y_SHORT_VECTOR is set, this bit describes the sign of the value, with 1 equaling positive and 0 negative.If Y_SHORT_VECTOR is not set and this bit is set, then the current y-coordinate is the same as the previous y-coordinate.If Y_SHORT_VECTOR is not set and this bit is also not set, the current y - coordinate is a signed 16 - bit delta vector. 118 | public const byte OVERLAP_SIMPLE = 0x40; // Bit 6: If set, contours in the glyph description may overlap.Use of this flag is not required in OpenType — that is, it is valid to have contours overlap without having this flag set. It may affect behaviors in some platforms, however. (See the discussion of “Overlapping contours” in Apple’s specification for details regarding behavior in Apple platforms.) When used, it must be set on the first flag byte for the glyph. See additional details below. 119 | public const byte SIMPLE_RESERVED = 0x80; // Bit 7 is reserved: set to zero. 120 | 121 | //Composite Glyph Flags 122 | public const ushort ARG_1_AND_2_ARE_WORDS = 0x0001; // Bit 0: If this is set, the arguments are 16 - bit(uint16 or int16); otherwise, they are bytes(uint8 or int8). 123 | public const ushort ARGS_ARE_XY_VALUES = 0x0002; // Bit 1: If this is set, the arguments are signed xy values; otherwise, they are unsigned point numbers. 124 | public const ushort ROUND_XY_TO_GRID = 0x0004; // Bit 2: For the xy values if the preceding is true. 125 | public const ushort WE_HAVE_A_SCALE = 0x0008; // Bit 3: This indicates that there is a simple scale for the component. Otherwise, scale = 1.0. 126 | public const ushort MORE_COMPONENTS = 0x0020; // Bit 5: Indicates at least one more glyph after this one. 127 | public const ushort WE_HAVE_AN_X_AND_Y_SCALE = 0x0040; // Bit 6: The x direction will use a different scale from the y direction. 128 | public const ushort WE_HAVE_A_TWO_BY_TWO = 0x0080; // Bit 7: There is a 2 by 2 transformation that will be used to scale the component. 129 | public const ushort WE_HAVE_INSTRUCTIONS = 0x0100; // Bit 8: Following the last component are instructions for the composite character. 130 | public const ushort USE_MY_METRICS = 0x0200; // Bit 9: If set, this forces the aw and lsb(and rsb) for the composite to be equal to those from this original glyph.This works for hinted and unhinted characters. 131 | public const ushort OVERLAP_COMPOUND = 0x0400; // Bit 10: If set, the components of the compound glyph overlap.Use of this flag is not required in OpenType — that is, it is valid to have components overlap without having this flag set.It may affect behaviors in some platforms, however. (See Apple’s specification for details regarding behavior in Apple platforms.) When used, it must be set on the flag word for the first component.See additional remarks, above, for the similar OVERLAP_SIMPLE flag used in simple - glyph descriptions. 132 | public const ushort SCALED_COMPONENT_OFFSET = 0x0800; // Bit 11: The composite is designed to have the component offset scaled. 133 | public const ushort UNSCALED_COMPONENT_OFFSET = 0x1000; // Bit 12: The composite is designed not to have the component offset scaled. 134 | public const ushort COMPOSITE_RESERVED = 0xE010; // Bits 4, 13, 14 and 15 are reserved: set to 0. 135 | 136 | public const string TagName = "glyf"; 137 | 138 | public short numberOfContours; // If the number of contours is greater than or equal to zero, this is a simple glyph. If negative, this is a composite glyph — the value -1 should be used for composite glyphs. 139 | public short xMin; // Minimum x for coordinate data. 140 | public short yMin; // Minimum y for coordinate data. 141 | public short xMax; // Maximum x for coordinate data. 142 | public short yMax; // Maximum y for coordinate data. 143 | 144 | // Simple Glyph 145 | public List endPtsOfCountours; // Array of point indices for the last point of each contour, in increasing numeric order. 146 | public ushort instructionLength; // Total number of bytes for instructions. If instructionLength is zero, no instructions are present for this glyph, and this field is followed directly by the flags field. 147 | public List instructions; // Array of instruction byte code for the glyph. 148 | public List simpflags; // Array of flag elements. See below for details regarding the number of flag array elements. 149 | public List xCoordinates; // Contour point x-coordinates. See below for details regarding the number of coordinate array elements. Coordinate for the first point is relative to (0,0); others are relative to previous point. 150 | public List yCoordinates; // Contour point y-coordinates. See below for details regarding the number of coordinate array elements. Coordinate for the first point is relative to (0,0); others are relative to previous point. 151 | 152 | public bool IsComplex { get => this.numberOfContours < 0; } // In general, values are complex; Ideally negative values should be -1 153 | 154 | public List compositeEntries; 155 | public ushort numInstr; 156 | public List instr; 157 | 158 | public void Read(FontReader r) 159 | { 160 | r.ReadInt(out this.numberOfContours); 161 | r.ReadInt(out this.xMin); 162 | r.ReadInt(out this.yMin); 163 | r.ReadInt(out this.xMax); 164 | r.ReadInt(out this.yMax); 165 | 166 | if (this.IsComplex == false) 167 | { 168 | // Simple 169 | 170 | this.endPtsOfCountours = new List(); 171 | for(int i = 0; i < this.numberOfContours; ++i) 172 | this.endPtsOfCountours.Add( r.ReadUInt16() ); 173 | 174 | r.ReadInt(out this.instructionLength); 175 | this.instructions = new List(); 176 | for(int i = 0; i < this.instructionLength; ++i) 177 | this.instructions.Add(r.ReadUInt8()); 178 | 179 | int numPoints = 0; 180 | // http://stevehanov.ca/blog/?id=143 181 | foreach(ushort us in this.endPtsOfCountours) 182 | numPoints = Mathf.Max(numPoints, us); 183 | numPoints += 1; 184 | 185 | this.simpflags = new List(); 186 | this.xCoordinates = new List(); 187 | this.yCoordinates = new List(); 188 | for (int i = 0; i < numPoints; ++i) 189 | { 190 | byte flag = r.ReadUInt8(); 191 | this.simpflags.Add(flag); 192 | 193 | if ((flag & REPEAT_FLAG) != 0) 194 | { 195 | byte repeatCount = r.ReadUInt8(); 196 | i += repeatCount; 197 | 198 | for (int j = 0; j < repeatCount; ++j) 199 | this.simpflags.Add(flag); 200 | 201 | } 202 | } 203 | 204 | int val = 0; 205 | for (int i = 0; i < numPoints; ++i) 206 | { 207 | byte flag = this.simpflags[i]; 208 | if ((flag & X_SHORT_VECTOR) != 0) 209 | { 210 | if ((flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) != 0) 211 | val += r.ReadUInt8(); 212 | else 213 | val -= r.ReadUInt8(); 214 | } 215 | else if ((~flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) != 0) 216 | val += r.ReadInt16(); 217 | 218 | this.xCoordinates.Add(val); 219 | } 220 | 221 | val = 0; 222 | for (int i = 0; i < numPoints; ++i) 223 | { 224 | byte flag = this.simpflags[i]; 225 | if ((flag & Y_SHORT_VECTOR) != 0) 226 | { 227 | if ((flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) != 0) 228 | val += r.ReadUInt8(); 229 | else 230 | val -= r.ReadUInt8(); 231 | } 232 | else if ((~flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) != 0) 233 | val += r.ReadInt16(); 234 | else 235 | { } // Value is unchanged 236 | 237 | this.yCoordinates.Add(val); 238 | } 239 | } 240 | else 241 | { 242 | // Composite 243 | this.compositeEntries = new List(); 244 | 245 | bool moreComps = true; 246 | while(moreComps == true) 247 | { 248 | CompositeEntry compE = new CompositeEntry(); 249 | moreComps = compE.Read(r); 250 | 251 | this.compositeEntries.Add(compE); 252 | } 253 | 254 | CompositeEntry ceLast = this.compositeEntries[compositeEntries.Count - 1]; 255 | if ((ceLast.flags & WE_HAVE_INSTRUCTIONS) != 0) 256 | { 257 | r.ReadInt(out this.numInstr); 258 | 259 | this.instr = new List(); 260 | for (int i = 0; i < this.instr.Count; ++i) 261 | this.instr.Add(r.ReadUInt8()); 262 | 263 | } 264 | } 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /Runtime/Font/Tables/Glyf.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4a02f35bf11c8244ba8687a804c8ca52 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Tables/Head.cs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Pixel Precision LLC 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | namespace Voxell.GPUVectorGraphics.Font 24 | { 25 | /// 26 | /// head — Font Header Table 27 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/head 28 | /// 29 | /// This table gives global information about the font. The bounding box values 30 | /// should be computed using only glyphs that have contours. Glyphs with no 31 | /// contours should be ignored for the purposes of these calculations. 32 | /// 33 | public struct Head 34 | { 35 | // A different style than the rest of the flags, but if they don't 36 | // give me text to explicitly copy, it's going to be a flag enum 37 | // with matching style. 38 | [System.Flags] 39 | public enum Flags 40 | { 41 | Baseline = (1 << 0), 42 | LeftSidebearing = (1 << 1), 43 | InstrDepOnPtSz = (1 << 2), 44 | ForcePPEMIntForScalar = (1 << 3), 45 | InstrMayAlterAdvWd = (1 << 4), 46 | Unused = (1 << 5), 47 | Lossless = (1 << 11), 48 | Converted = (1 << 12), 49 | Optimized = (1 << 13), 50 | LastResort = (1 << 14), 51 | } 52 | 53 | [System.Flags] 54 | public enum MacStyle 55 | { 56 | Bold = (1 << 0), 57 | Italic = (1 << 1), 58 | Underline = (1 << 2), 59 | Outline = (1 << 3), 60 | Shadow = (1 << 4), 61 | Condensed = (1 << 5), 62 | Extended = (1 << 6) 63 | } 64 | 65 | public const string TagName = "head"; 66 | 67 | // See member head.magicNumber. 68 | public const int ExpectedMagicNumber = 0x5F0F3CF5; 69 | 70 | public ushort majorVersion; // Major version number of the font header table — set to 1. 71 | public ushort minorVersion; // Minor version number of the font header table — set to 0. 72 | public float fontRevision; // Set by font manufacturer. 73 | public uint checksumAdjustment; // To compute: set it to 0, sum the entire font as uint32, then store 0xB1B0AFBA - sum. If the font is used as a component in a font collection file, the value of this field will be invalidated by changes to the file structure and font table directory, and must be ignored. 74 | public uint magicNumber; // Set to 0x5F0F3CF5. 75 | public ushort flags; 76 | public ushort unitsPerEm; // Set to a value from 16 to 16384. Any value in this range is valid. In fonts that have TrueType outlines, a power of 2 is recommended as this allows performance optimizations in some rasterizers. 77 | public System.DateTime created; // Number of seconds since 12:00 midnight that started January 1st 1904 in GMT/UTC time zone. 78 | public System.DateTime modified; // Number of seconds since 12:00 midnight that started January 1st 1904 in GMT/UTC time zone. 79 | public short xMin; // For all glyph bounding boxes. (Glyphs without contours are ignored.) 80 | public short yMin; // For all glyph bounding boxes. (Glyphs without contours are ignored.) 81 | public short xMax; // For all glyph bounding boxes. (Glyphs without contours are ignored.) 82 | public short yMax; // For all glyph bounding boxes. (Glyphs without contours are ignored.) 83 | public ushort macStyle; // 84 | public ushort lowestRecPPEM; // Smallest readable size in pixels. 85 | public ushort fontDirectionHint; // 86 | public short indexToLocFormat; // 0 for short offsets (Offset16), 1 for long (Offset32). 87 | public short glyphDataFormat; // 0 for current format. 88 | 89 | public int OffsetByteWidth 90 | { 91 | get 92 | { 93 | switch (this.indexToLocFormat) 94 | { 95 | case 0: 96 | return 2; // Offset16 / uint16 97 | case 1: 98 | return 4; // Offset32 / uint32 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | } 105 | 106 | public void Read(FontReader r) 107 | { 108 | r.ReadInt(out this.majorVersion); 109 | r.ReadInt(out this.minorVersion); 110 | this.fontRevision = r.ReadFixed(); 111 | r.ReadInt(out this.checksumAdjustment); 112 | r.ReadInt(out this.magicNumber); 113 | r.ReadInt(out this.flags); 114 | r.ReadInt(out this.unitsPerEm); 115 | this.created = r.ReadDate(); 116 | this.modified = r.ReadDate(); 117 | r.ReadInt(out this.xMin); 118 | r.ReadInt(out this.yMin); 119 | r.ReadInt(out this.xMax); 120 | r.ReadInt(out this.yMax); 121 | r.ReadInt(out this.macStyle); 122 | r.ReadInt(out this.lowestRecPPEM); 123 | r.ReadInt(out this.fontDirectionHint); 124 | r.ReadInt(out this.indexToLocFormat); 125 | r.ReadInt(out this.glyphDataFormat); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /Runtime/Font/Tables/Head.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f9fca748297479c4d8ced97c8d5abf19 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Tables/Loca.cs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Pixel Precision LLC 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | using System.Collections.Generic; 24 | 25 | namespace Voxell.GPUVectorGraphics.Font 26 | { 27 | /// 28 | /// loca — Index to Location 29 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/loca 30 | /// 31 | /// The indexToLoc table stores the offsets to the locations of the glyphs 32 | /// in the font, relative to the beginning of the glyphData table. In order 33 | /// to compute the length of the last glyph element, there is an extra entry 34 | /// after the last valid index. 35 | /// 36 | public struct Loca 37 | { 38 | public const string TagName = "loca"; 39 | 40 | // If int16: 41 | // The actual local offset divided by 2 is stored. The value of n 42 | // is numGlyphs + 1. The value for numGlyphs is found in the 'maxp' table. 43 | // If Int32: 44 | // The actual local offset is stored. The value of n is numGlyphs + 1. 45 | // The value for numGlyphs is found in the 'maxp' table. 46 | public List offset; 47 | 48 | public void Read(FontReader r, int numGlyphs, bool longVer) 49 | { 50 | int readCt = numGlyphs + 1; 51 | this.offset = new List(); 52 | 53 | if (longVer == false) 54 | { 55 | for (int i = 0; i < readCt; ++i) 56 | this.offset.Add( (uint)(r.ReadUInt16() * 2)); 57 | } 58 | else 59 | { 60 | for (int i = 0; i < readCt; ++i) 61 | this.offset.Add( r.ReadUInt32()); 62 | } 63 | } 64 | 65 | public int GetGlyphCount() => this.offset.Count - 1; 66 | 67 | public uint GetGlyphSize(int idx) => this.offset[idx + 1] - this.offset[idx]; 68 | 69 | public uint GetGlyphOffset(Table tableGlyf, int idx) => tableGlyf.offset + this.offset[idx]; 70 | } 71 | } -------------------------------------------------------------------------------- /Runtime/Font/Tables/Loca.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 653f7c03ac620c349887a162580fb1ef 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Tables/MaxP.cs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Pixel Precision LLC 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | using System.Collections; 24 | using System.Collections.Generic; 25 | using UnityEngine; 26 | 27 | namespace Voxell.GPUVectorGraphics.Font 28 | { 29 | /// 30 | /// maxp — Maximum Profile 31 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/maxp 32 | /// 33 | /// This table establishes the memory requirements for this font. Fonts with CFF 34 | /// data must use Version 0.5 of this table, specifying only the numGlyphs field. 35 | /// Fonts with TrueType outlines must use Version 1.0 of this table, where all 36 | /// data is required. 37 | /// 38 | public struct Maxp 39 | { 40 | public const string TagName = "maxp"; 41 | 42 | public ushort majorVersion; 43 | public ushort minorVersion; 44 | public ushort numGlyphs; 45 | 46 | public ushort maxPoints; 47 | public ushort maxCountours; 48 | public ushort maxCompositePoints; 49 | public ushort maxCompositeContours; 50 | public ushort maxZones; 51 | public ushort maxTwilightPoints; 52 | public ushort maxStorage; 53 | public ushort maxFunctionDefs; 54 | public ushort maxIInstructionDefs; 55 | public ushort maxStackElements; 56 | public ushort maxSizeOfInstructions; 57 | public ushort maxComponentElements; 58 | public ushort maxComponentDepth; 59 | 60 | public void Read(FontReader r) 61 | { 62 | r.ReadInt(out this.majorVersion); 63 | r.ReadInt(out this.minorVersion); 64 | r.ReadInt(out this.numGlyphs); 65 | 66 | if (majorVersion == 0) return; 67 | 68 | r.ReadInt(out this.maxPoints); 69 | r.ReadInt(out this.maxCountours); 70 | r.ReadInt(out this.maxCompositePoints); 71 | r.ReadInt(out this.maxCompositeContours); 72 | r.ReadInt(out this.maxTwilightPoints); 73 | r.ReadInt(out this.maxStorage); 74 | r.ReadInt(out this.maxFunctionDefs); 75 | r.ReadInt(out this.maxIInstructionDefs); 76 | r.ReadInt(out this.maxStackElements); 77 | r.ReadInt(out this.maxSizeOfInstructions); 78 | r.ReadInt(out this.maxComponentElements); 79 | r.ReadInt(out this.maxComponentDepth); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Runtime/Font/Tables/MaxP.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d83af5dedf599eb41ade6f24b6e3549d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/Tables/Table.cs: -------------------------------------------------------------------------------- 1 | namespace Voxell.GPUVectorGraphics.Font 2 | { 3 | [System.Serializable] 4 | public struct Table 5 | { 6 | /// The 4 byte identifier. 7 | public string tag; 8 | 9 | /// The error detection checksum. 10 | public uint checksum; 11 | 12 | /// The offset from the start of the file, where the actual data payload is. 13 | public uint offset; 14 | 15 | /// The size, in bytes, of the data payload. 16 | public uint length; 17 | 18 | /// Read the table from a TTFReader. 19 | /// The reader. 20 | public void Read(FontReader r) 21 | { 22 | this.tag = r.ReadString(4); 23 | r.ReadInt(out this.checksum); 24 | r.ReadInt(out this.offset); 25 | r.ReadInt(out this.length); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Runtime/Font/Tables/Table.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d678988eb016f7f49a5ea2a941a6af27 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Font/TypeFace.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Voxell.GPUVectorGraphics.Font 4 | { 5 | [System.Serializable] 6 | public struct TypeFace 7 | { 8 | [Tooltip("Name of the type face.")] 9 | public string name; 10 | 11 | [Tooltip("Bezier contour for each character.")] 12 | public Glyph[] glyphs; 13 | } 14 | } -------------------------------------------------------------------------------- /Runtime/Font/TypeFace.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f2a57febe1efbd94ab8798c1990cef28 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/QuadraticBezier.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad0a127910db2f84ba2904c88a9f1105 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/VGMath.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Unity.Mathematics; 3 | 4 | namespace Voxell.GPUVectorGraphics 5 | { 6 | internal static class VGMath 7 | { 8 | /// Determines if a point is inside a triangle. 9 | /// 10 | /// https://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle 11 | /// 12 | internal static bool PointInTriangle(float2 p, float2 p0, float2 p1, float2 p2) 13 | { 14 | float d1, d2, d3; 15 | bool hasNeg, hasPos; 16 | 17 | d1 = VertEdgeSign(p, p0, p1); 18 | d2 = VertEdgeSign(p, p1, p2); 19 | d3 = VertEdgeSign(p, p2, p0); 20 | 21 | hasNeg = (d1 < 0.0f) || (d2 < 0.0f) || (d3 < 0.0f); 22 | hasPos = (d1 > 0.0f) || (d2 > 0.0f) || (d3 > 0.0f); 23 | 24 | return !(hasNeg && hasPos); 25 | } 26 | 27 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 28 | internal static float VertEdgeSign(float2 p, float2 p0, float2 p1) 29 | => (p.x - p1.x) * (p0.y - p1.y) - (p0.x - p1.x) * (p.y - p1.y); 30 | 31 | internal static bool LinesIntersect(float2 p1, float2 q1, float2 p2, float2 q2) 32 | { 33 | return (Orientation(p1, q1, p2) != Orientation(p1, q1, q2) 34 | && Orientation(p2, q2, p1) != Orientation(p2, q2, q1)); 35 | } 36 | 37 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 38 | internal static int Orientation(float2 p1, float2 p2, float2 p3) 39 | { 40 | float crossProduct = (p2.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p2.x - p1.x); 41 | return (crossProduct < 0.0f) ? -1 : ((crossProduct > 0.0f) ? 1 : 0); 42 | } 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | internal static bool IsClockwise(in float2 p0, in float2 p1, in float2 p2) 46 | { 47 | return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y) < 0.0f; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Runtime/VGMath.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dfa13ecdb4a8b1d41882aad12770ccc3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/VX.GPUVectorGraphics.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VX.GPUVectorGraphics.Runtime", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:d8b63aba1907145bea998dd612889d6b", 6 | "GUID:e0cd26848372d4e5c891c569017e11f1", 7 | "GUID:8a2eafa29b15f444eb6d74f94a930e1d", 8 | "GUID:2665a8d13d1b3f18800f46e256720795", 9 | "GUID:b23efe0d0bd83184681bd63517e3c327" 10 | ], 11 | "includePlatforms": [], 12 | "excludePlatforms": [], 13 | "allowUnsafeCode": false, 14 | "overrideReferences": false, 15 | "precompiledReferences": [], 16 | "autoReferenced": true, 17 | "defineConstraints": [], 18 | "versionDefines": [], 19 | "noEngineReferences": false 20 | } -------------------------------------------------------------------------------- /Runtime/VX.GPUVectorGraphics.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: da829e25e5d77364984881df942d9735 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e1263278677204d438e7cfab4e2ba3f2 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Shader/CubicBezier.shadersubgraph: -------------------------------------------------------------------------------- 1 | { 2 | "m_SGVersion": 3, 3 | "m_Type": "UnityEditor.ShaderGraph.GraphData", 4 | "m_ObjectId": "91f4c36ee2c7400da69de5770a04b551", 5 | "m_Properties": [ 6 | { 7 | "m_Id": "b74f60ff52314eaf9d500f61acca891d" 8 | } 9 | ], 10 | "m_Keywords": [], 11 | "m_Dropdowns": [], 12 | "m_CategoryData": [ 13 | { 14 | "m_Id": "55bdd673903d418fbb2a02c737ce61bc" 15 | } 16 | ], 17 | "m_Nodes": [ 18 | { 19 | "m_Id": "dd2ba1bba9e54564a6ec2ac8b267fbd8" 20 | }, 21 | { 22 | "m_Id": "b488542ded4b4807a9668cd872b2bbcb" 23 | }, 24 | { 25 | "m_Id": "0bdee68fe9864b67b899070bf6a6ca90" 26 | }, 27 | { 28 | "m_Id": "faa4cd12c9814b6085e980b39a269ddd" 29 | }, 30 | { 31 | "m_Id": "185412b1432e4b1c82d020206ad46f2c" 32 | }, 33 | { 34 | "m_Id": "5b9608185e4744b4bde11968a54a41eb" 35 | }, 36 | { 37 | "m_Id": "f742d872c3644c91a7e7d0e40377ac4b" 38 | }, 39 | { 40 | "m_Id": "f9544d8c79c949e0985fd23aba80a7ae" 41 | } 42 | ], 43 | "m_GroupDatas": [], 44 | "m_StickyNoteDatas": [], 45 | "m_Edges": [ 46 | { 47 | "m_OutputSlot": { 48 | "m_Node": { 49 | "m_Id": "0bdee68fe9864b67b899070bf6a6ca90" 50 | }, 51 | "m_SlotId": 1 52 | }, 53 | "m_InputSlot": { 54 | "m_Node": { 55 | "m_Id": "faa4cd12c9814b6085e980b39a269ddd" 56 | }, 57 | "m_SlotId": 0 58 | } 59 | }, 60 | { 61 | "m_OutputSlot": { 62 | "m_Node": { 63 | "m_Id": "0bdee68fe9864b67b899070bf6a6ca90" 64 | }, 65 | "m_SlotId": 1 66 | }, 67 | "m_InputSlot": { 68 | "m_Node": { 69 | "m_Id": "faa4cd12c9814b6085e980b39a269ddd" 70 | }, 71 | "m_SlotId": 1 72 | } 73 | }, 74 | { 75 | "m_OutputSlot": { 76 | "m_Node": { 77 | "m_Id": "0bdee68fe9864b67b899070bf6a6ca90" 78 | }, 79 | "m_SlotId": 2 80 | }, 81 | "m_InputSlot": { 82 | "m_Node": { 83 | "m_Id": "f742d872c3644c91a7e7d0e40377ac4b" 84 | }, 85 | "m_SlotId": 1 86 | } 87 | }, 88 | { 89 | "m_OutputSlot": { 90 | "m_Node": { 91 | "m_Id": "0bdee68fe9864b67b899070bf6a6ca90" 92 | }, 93 | "m_SlotId": 3 94 | }, 95 | "m_InputSlot": { 96 | "m_Node": { 97 | "m_Id": "5b9608185e4744b4bde11968a54a41eb" 98 | }, 99 | "m_SlotId": 1 100 | } 101 | }, 102 | { 103 | "m_OutputSlot": { 104 | "m_Node": { 105 | "m_Id": "185412b1432e4b1c82d020206ad46f2c" 106 | }, 107 | "m_SlotId": 2 108 | }, 109 | "m_InputSlot": { 110 | "m_Node": { 111 | "m_Id": "dd2ba1bba9e54564a6ec2ac8b267fbd8" 112 | }, 113 | "m_SlotId": 1 114 | } 115 | }, 116 | { 117 | "m_OutputSlot": { 118 | "m_Node": { 119 | "m_Id": "5b9608185e4744b4bde11968a54a41eb" 120 | }, 121 | "m_SlotId": 2 122 | }, 123 | "m_InputSlot": { 124 | "m_Node": { 125 | "m_Id": "185412b1432e4b1c82d020206ad46f2c" 126 | }, 127 | "m_SlotId": 0 128 | } 129 | }, 130 | { 131 | "m_OutputSlot": { 132 | "m_Node": { 133 | "m_Id": "b488542ded4b4807a9668cd872b2bbcb" 134 | }, 135 | "m_SlotId": 0 136 | }, 137 | "m_InputSlot": { 138 | "m_Node": { 139 | "m_Id": "0bdee68fe9864b67b899070bf6a6ca90" 140 | }, 141 | "m_SlotId": 0 142 | } 143 | }, 144 | { 145 | "m_OutputSlot": { 146 | "m_Node": { 147 | "m_Id": "f742d872c3644c91a7e7d0e40377ac4b" 148 | }, 149 | "m_SlotId": 2 150 | }, 151 | "m_InputSlot": { 152 | "m_Node": { 153 | "m_Id": "5b9608185e4744b4bde11968a54a41eb" 154 | }, 155 | "m_SlotId": 0 156 | } 157 | }, 158 | { 159 | "m_OutputSlot": { 160 | "m_Node": { 161 | "m_Id": "f9544d8c79c949e0985fd23aba80a7ae" 162 | }, 163 | "m_SlotId": 0 164 | }, 165 | "m_InputSlot": { 166 | "m_Node": { 167 | "m_Id": "185412b1432e4b1c82d020206ad46f2c" 168 | }, 169 | "m_SlotId": 1 170 | } 171 | }, 172 | { 173 | "m_OutputSlot": { 174 | "m_Node": { 175 | "m_Id": "faa4cd12c9814b6085e980b39a269ddd" 176 | }, 177 | "m_SlotId": 2 178 | }, 179 | "m_InputSlot": { 180 | "m_Node": { 181 | "m_Id": "f742d872c3644c91a7e7d0e40377ac4b" 182 | }, 183 | "m_SlotId": 0 184 | } 185 | } 186 | ], 187 | "m_VertexContext": { 188 | "m_Position": { 189 | "x": 0.0, 190 | "y": 0.0 191 | }, 192 | "m_Blocks": [] 193 | }, 194 | "m_FragmentContext": { 195 | "m_Position": { 196 | "x": 0.0, 197 | "y": 0.0 198 | }, 199 | "m_Blocks": [] 200 | }, 201 | "m_PreviewData": { 202 | "serializedMesh": { 203 | "m_SerializedMesh": "{\"mesh\":{\"instanceID\":0}}", 204 | "m_Guid": "" 205 | }, 206 | "preventRotation": false 207 | }, 208 | "m_Path": "Sub Graphs", 209 | "m_GraphPrecision": 1, 210 | "m_PreviewMode": 2, 211 | "m_OutputNode": { 212 | "m_Id": "dd2ba1bba9e54564a6ec2ac8b267fbd8" 213 | }, 214 | "m_ActiveTargets": [] 215 | } 216 | 217 | { 218 | "m_SGVersion": 0, 219 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 220 | "m_ObjectId": "07467ea7feda44ceade4aca1fb57bc32", 221 | "m_Id": 0, 222 | "m_DisplayName": "A", 223 | "m_SlotType": 0, 224 | "m_Hidden": false, 225 | "m_ShaderOutputName": "A", 226 | "m_StageCapability": 3, 227 | "m_Value": { 228 | "e00": 0.0, 229 | "e01": 0.0, 230 | "e02": 0.0, 231 | "e03": 0.0, 232 | "e10": 0.0, 233 | "e11": 0.0, 234 | "e12": 0.0, 235 | "e13": 0.0, 236 | "e20": 0.0, 237 | "e21": 0.0, 238 | "e22": 0.0, 239 | "e23": 0.0, 240 | "e30": 0.0, 241 | "e31": 0.0, 242 | "e32": 0.0, 243 | "e33": 0.0 244 | }, 245 | "m_DefaultValue": { 246 | "e00": 1.0, 247 | "e01": 0.0, 248 | "e02": 0.0, 249 | "e03": 0.0, 250 | "e10": 0.0, 251 | "e11": 1.0, 252 | "e12": 0.0, 253 | "e13": 0.0, 254 | "e20": 0.0, 255 | "e21": 0.0, 256 | "e22": 1.0, 257 | "e23": 0.0, 258 | "e30": 0.0, 259 | "e31": 0.0, 260 | "e32": 0.0, 261 | "e33": 1.0 262 | } 263 | } 264 | 265 | { 266 | "m_SGVersion": 0, 267 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 268 | "m_ObjectId": "08b49400458d41249bceb741fd8ead60", 269 | "m_Id": 1, 270 | "m_DisplayName": "B", 271 | "m_SlotType": 0, 272 | "m_Hidden": false, 273 | "m_ShaderOutputName": "B", 274 | "m_StageCapability": 3, 275 | "m_Value": { 276 | "e00": 2.0, 277 | "e01": 2.0, 278 | "e02": 2.0, 279 | "e03": 2.0, 280 | "e10": 2.0, 281 | "e11": 2.0, 282 | "e12": 2.0, 283 | "e13": 2.0, 284 | "e20": 2.0, 285 | "e21": 2.0, 286 | "e22": 2.0, 287 | "e23": 2.0, 288 | "e30": 2.0, 289 | "e31": 2.0, 290 | "e32": 2.0, 291 | "e33": 2.0 292 | }, 293 | "m_DefaultValue": { 294 | "e00": 1.0, 295 | "e01": 0.0, 296 | "e02": 0.0, 297 | "e03": 0.0, 298 | "e10": 0.0, 299 | "e11": 1.0, 300 | "e12": 0.0, 301 | "e13": 0.0, 302 | "e20": 0.0, 303 | "e21": 0.0, 304 | "e22": 1.0, 305 | "e23": 0.0, 306 | "e30": 0.0, 307 | "e31": 0.0, 308 | "e32": 0.0, 309 | "e33": 1.0 310 | } 311 | } 312 | 313 | { 314 | "m_SGVersion": 0, 315 | "m_Type": "UnityEditor.ShaderGraph.SplitNode", 316 | "m_ObjectId": "0bdee68fe9864b67b899070bf6a6ca90", 317 | "m_Group": { 318 | "m_Id": "" 319 | }, 320 | "m_Name": "Split", 321 | "m_DrawState": { 322 | "m_Expanded": false, 323 | "m_Position": { 324 | "serializedVersion": "2", 325 | "x": -107.0, 326 | "y": 0.0, 327 | "width": 120.0, 328 | "height": 125.0 329 | } 330 | }, 331 | "m_Slots": [ 332 | { 333 | "m_Id": "40f13ec0963c45c4937abd23650aee74" 334 | }, 335 | { 336 | "m_Id": "896f1a3ba19b4d7db9a6c9b846d1e8cc" 337 | }, 338 | { 339 | "m_Id": "57d09045a0d4458b94cc43a1dd83849a" 340 | }, 341 | { 342 | "m_Id": "78dafad169fe4b8eb2751c91405aa6fb" 343 | }, 344 | { 345 | "m_Id": "e3a7322b4e94408280104599e55afac6" 346 | } 347 | ], 348 | "synonyms": [ 349 | "separate" 350 | ], 351 | "m_Precision": 0, 352 | "m_PreviewExpanded": true, 353 | "m_PreviewMode": 0, 354 | "m_CustomColors": { 355 | "m_SerializableColors": [] 356 | } 357 | } 358 | 359 | { 360 | "m_SGVersion": 0, 361 | "m_Type": "UnityEditor.ShaderGraph.MultiplyNode", 362 | "m_ObjectId": "185412b1432e4b1c82d020206ad46f2c", 363 | "m_Group": { 364 | "m_Id": "" 365 | }, 366 | "m_Name": "Multiply", 367 | "m_DrawState": { 368 | "m_Expanded": true, 369 | "m_Position": { 370 | "serializedVersion": "2", 371 | "x": 391.0, 372 | "y": 0.0, 373 | "width": 126.0, 374 | "height": 118.0 375 | } 376 | }, 377 | "m_Slots": [ 378 | { 379 | "m_Id": "07467ea7feda44ceade4aca1fb57bc32" 380 | }, 381 | { 382 | "m_Id": "698f7999248043a3adb36f8b2d57a306" 383 | }, 384 | { 385 | "m_Id": "ceaa2f3025424970991f1923d1e82c05" 386 | } 387 | ], 388 | "synonyms": [ 389 | "multiplication", 390 | "times", 391 | "x" 392 | ], 393 | "m_Precision": 0, 394 | "m_PreviewExpanded": false, 395 | "m_PreviewMode": 0, 396 | "m_CustomColors": { 397 | "m_SerializableColors": [] 398 | } 399 | } 400 | 401 | { 402 | "m_SGVersion": 0, 403 | "m_Type": "UnityEditor.ShaderGraph.DynamicVectorMaterialSlot", 404 | "m_ObjectId": "3fde0b7067d845198b86fe7096dc76bc", 405 | "m_Id": 0, 406 | "m_DisplayName": "A", 407 | "m_SlotType": 0, 408 | "m_Hidden": false, 409 | "m_ShaderOutputName": "A", 410 | "m_StageCapability": 3, 411 | "m_Value": { 412 | "x": 1.0, 413 | "y": 1.0, 414 | "z": 1.0, 415 | "w": 1.0 416 | }, 417 | "m_DefaultValue": { 418 | "x": 0.0, 419 | "y": 0.0, 420 | "z": 0.0, 421 | "w": 0.0 422 | } 423 | } 424 | 425 | { 426 | "m_SGVersion": 0, 427 | "m_Type": "UnityEditor.ShaderGraph.DynamicVectorMaterialSlot", 428 | "m_ObjectId": "40f13ec0963c45c4937abd23650aee74", 429 | "m_Id": 0, 430 | "m_DisplayName": "In", 431 | "m_SlotType": 0, 432 | "m_Hidden": false, 433 | "m_ShaderOutputName": "In", 434 | "m_StageCapability": 3, 435 | "m_Value": { 436 | "x": 0.0, 437 | "y": 0.0, 438 | "z": 0.0, 439 | "w": 0.0 440 | }, 441 | "m_DefaultValue": { 442 | "x": 0.0, 443 | "y": 0.0, 444 | "z": 0.0, 445 | "w": 0.0 446 | } 447 | } 448 | 449 | { 450 | "m_SGVersion": 0, 451 | "m_Type": "UnityEditor.ShaderGraph.DynamicVectorMaterialSlot", 452 | "m_ObjectId": "462e0d8c5bca4d7bb89e85d1ca15c5ae", 453 | "m_Id": 1, 454 | "m_DisplayName": "B", 455 | "m_SlotType": 0, 456 | "m_Hidden": false, 457 | "m_ShaderOutputName": "B", 458 | "m_StageCapability": 3, 459 | "m_Value": { 460 | "x": 1.0, 461 | "y": 1.0, 462 | "z": 1.0, 463 | "w": 1.0 464 | }, 465 | "m_DefaultValue": { 466 | "x": 0.0, 467 | "y": 0.0, 468 | "z": 0.0, 469 | "w": 0.0 470 | } 471 | } 472 | 473 | { 474 | "m_SGVersion": 0, 475 | "m_Type": "UnityEditor.ShaderGraph.CategoryData", 476 | "m_ObjectId": "55bdd673903d418fbb2a02c737ce61bc", 477 | "m_Name": "", 478 | "m_ChildObjectList": [ 479 | { 480 | "m_Id": "b74f60ff52314eaf9d500f61acca891d" 481 | } 482 | ] 483 | } 484 | 485 | { 486 | "m_SGVersion": 0, 487 | "m_Type": "UnityEditor.ShaderGraph.DynamicVectorMaterialSlot", 488 | "m_ObjectId": "5623051ea6094598a798d302a4fbca20", 489 | "m_Id": 2, 490 | "m_DisplayName": "Out", 491 | "m_SlotType": 1, 492 | "m_Hidden": false, 493 | "m_ShaderOutputName": "Out", 494 | "m_StageCapability": 3, 495 | "m_Value": { 496 | "x": 0.0, 497 | "y": 0.0, 498 | "z": 0.0, 499 | "w": 0.0 500 | }, 501 | "m_DefaultValue": { 502 | "x": 0.0, 503 | "y": 0.0, 504 | "z": 0.0, 505 | "w": 0.0 506 | } 507 | } 508 | 509 | { 510 | "m_SGVersion": 0, 511 | "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", 512 | "m_ObjectId": "57d09045a0d4458b94cc43a1dd83849a", 513 | "m_Id": 2, 514 | "m_DisplayName": "G", 515 | "m_SlotType": 1, 516 | "m_Hidden": false, 517 | "m_ShaderOutputName": "G", 518 | "m_StageCapability": 3, 519 | "m_Value": 0.0, 520 | "m_DefaultValue": 0.0, 521 | "m_Labels": [] 522 | } 523 | 524 | { 525 | "m_SGVersion": 0, 526 | "m_Type": "UnityEditor.ShaderGraph.MultiplyNode", 527 | "m_ObjectId": "5b9608185e4744b4bde11968a54a41eb", 528 | "m_Group": { 529 | "m_Id": "" 530 | }, 531 | "m_Name": "Multiply", 532 | "m_DrawState": { 533 | "m_Expanded": true, 534 | "m_Position": { 535 | "serializedVersion": "2", 536 | "x": 265.0, 537 | "y": 0.0, 538 | "width": 126.0, 539 | "height": 118.0 540 | } 541 | }, 542 | "m_Slots": [ 543 | { 544 | "m_Id": "7f983f0255cd4c759f961f772e933ed6" 545 | }, 546 | { 547 | "m_Id": "08b49400458d41249bceb741fd8ead60" 548 | }, 549 | { 550 | "m_Id": "c1c025b47abe498cadd08fefb4e4236f" 551 | } 552 | ], 553 | "synonyms": [ 554 | "multiplication", 555 | "times", 556 | "x" 557 | ], 558 | "m_Precision": 0, 559 | "m_PreviewExpanded": false, 560 | "m_PreviewMode": 0, 561 | "m_CustomColors": { 562 | "m_SerializableColors": [] 563 | } 564 | } 565 | 566 | { 567 | "m_SGVersion": 0, 568 | "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", 569 | "m_ObjectId": "698f1e732a9d4a4ba2af85fb8f6d9dfc", 570 | "m_Id": 0, 571 | "m_DisplayName": "Out", 572 | "m_SlotType": 1, 573 | "m_Hidden": false, 574 | "m_ShaderOutputName": "Out", 575 | "m_StageCapability": 3, 576 | "m_Value": { 577 | "x": 0.0, 578 | "y": 0.0, 579 | "z": 0.0, 580 | "w": 0.0 581 | }, 582 | "m_DefaultValue": { 583 | "x": 0.0, 584 | "y": 0.0, 585 | "z": 0.0, 586 | "w": 0.0 587 | }, 588 | "m_Labels": [] 589 | } 590 | 591 | { 592 | "m_SGVersion": 0, 593 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 594 | "m_ObjectId": "698f7999248043a3adb36f8b2d57a306", 595 | "m_Id": 1, 596 | "m_DisplayName": "B", 597 | "m_SlotType": 0, 598 | "m_Hidden": false, 599 | "m_ShaderOutputName": "B", 600 | "m_StageCapability": 3, 601 | "m_Value": { 602 | "e00": 2.0, 603 | "e01": 2.0, 604 | "e02": 2.0, 605 | "e03": 2.0, 606 | "e10": 2.0, 607 | "e11": 2.0, 608 | "e12": 2.0, 609 | "e13": 2.0, 610 | "e20": 2.0, 611 | "e21": 2.0, 612 | "e22": 2.0, 613 | "e23": 2.0, 614 | "e30": 2.0, 615 | "e31": 2.0, 616 | "e32": 2.0, 617 | "e33": 2.0 618 | }, 619 | "m_DefaultValue": { 620 | "e00": 1.0, 621 | "e01": 0.0, 622 | "e02": 0.0, 623 | "e03": 0.0, 624 | "e10": 0.0, 625 | "e11": 1.0, 626 | "e12": 0.0, 627 | "e13": 0.0, 628 | "e20": 0.0, 629 | "e21": 0.0, 630 | "e22": 1.0, 631 | "e23": 0.0, 632 | "e30": 0.0, 633 | "e31": 0.0, 634 | "e32": 0.0, 635 | "e33": 1.0 636 | } 637 | } 638 | 639 | { 640 | "m_SGVersion": 0, 641 | "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", 642 | "m_ObjectId": "78dafad169fe4b8eb2751c91405aa6fb", 643 | "m_Id": 3, 644 | "m_DisplayName": "B", 645 | "m_SlotType": 1, 646 | "m_Hidden": false, 647 | "m_ShaderOutputName": "B", 648 | "m_StageCapability": 3, 649 | "m_Value": 0.0, 650 | "m_DefaultValue": 0.0, 651 | "m_Labels": [] 652 | } 653 | 654 | { 655 | "m_SGVersion": 0, 656 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 657 | "m_ObjectId": "7f983f0255cd4c759f961f772e933ed6", 658 | "m_Id": 0, 659 | "m_DisplayName": "A", 660 | "m_SlotType": 0, 661 | "m_Hidden": false, 662 | "m_ShaderOutputName": "A", 663 | "m_StageCapability": 3, 664 | "m_Value": { 665 | "e00": 0.0, 666 | "e01": 0.0, 667 | "e02": 0.0, 668 | "e03": 0.0, 669 | "e10": 0.0, 670 | "e11": 0.0, 671 | "e12": 0.0, 672 | "e13": 0.0, 673 | "e20": 0.0, 674 | "e21": 0.0, 675 | "e22": 0.0, 676 | "e23": 0.0, 677 | "e30": 0.0, 678 | "e31": 0.0, 679 | "e32": 0.0, 680 | "e33": 0.0 681 | }, 682 | "m_DefaultValue": { 683 | "e00": 1.0, 684 | "e01": 0.0, 685 | "e02": 0.0, 686 | "e03": 0.0, 687 | "e10": 0.0, 688 | "e11": 1.0, 689 | "e12": 0.0, 690 | "e13": 0.0, 691 | "e20": 0.0, 692 | "e21": 0.0, 693 | "e22": 1.0, 694 | "e23": 0.0, 695 | "e30": 0.0, 696 | "e31": 0.0, 697 | "e32": 0.0, 698 | "e33": 1.0 699 | } 700 | } 701 | 702 | { 703 | "m_SGVersion": 0, 704 | "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", 705 | "m_ObjectId": "896f1a3ba19b4d7db9a6c9b846d1e8cc", 706 | "m_Id": 1, 707 | "m_DisplayName": "R", 708 | "m_SlotType": 1, 709 | "m_Hidden": false, 710 | "m_ShaderOutputName": "R", 711 | "m_StageCapability": 3, 712 | "m_Value": 0.0, 713 | "m_DefaultValue": 0.0, 714 | "m_Labels": [] 715 | } 716 | 717 | { 718 | "m_SGVersion": 0, 719 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 720 | "m_ObjectId": "b1bc7f5858ea4eb8ab5cd007c8f30439", 721 | "m_Id": 0, 722 | "m_DisplayName": "A", 723 | "m_SlotType": 0, 724 | "m_Hidden": false, 725 | "m_ShaderOutputName": "A", 726 | "m_StageCapability": 3, 727 | "m_Value": { 728 | "e00": 0.0, 729 | "e01": 0.0, 730 | "e02": 0.0, 731 | "e03": 0.0, 732 | "e10": 0.0, 733 | "e11": 0.0, 734 | "e12": 0.0, 735 | "e13": 0.0, 736 | "e20": 0.0, 737 | "e21": 0.0, 738 | "e22": 0.0, 739 | "e23": 0.0, 740 | "e30": 0.0, 741 | "e31": 0.0, 742 | "e32": 0.0, 743 | "e33": 0.0 744 | }, 745 | "m_DefaultValue": { 746 | "e00": 1.0, 747 | "e01": 0.0, 748 | "e02": 0.0, 749 | "e03": 0.0, 750 | "e10": 0.0, 751 | "e11": 1.0, 752 | "e12": 0.0, 753 | "e13": 0.0, 754 | "e20": 0.0, 755 | "e21": 0.0, 756 | "e22": 1.0, 757 | "e23": 0.0, 758 | "e30": 0.0, 759 | "e31": 0.0, 760 | "e32": 0.0, 761 | "e33": 1.0 762 | } 763 | } 764 | 765 | { 766 | "m_SGVersion": 0, 767 | "m_Type": "UnityEditor.ShaderGraph.UVNode", 768 | "m_ObjectId": "b488542ded4b4807a9668cd872b2bbcb", 769 | "m_Group": { 770 | "m_Id": "" 771 | }, 772 | "m_Name": "UV", 773 | "m_DrawState": { 774 | "m_Expanded": true, 775 | "m_Position": { 776 | "serializedVersion": "2", 777 | "x": -252.0, 778 | "y": 0.0, 779 | "width": 145.0, 780 | "height": 129.0 781 | } 782 | }, 783 | "m_Slots": [ 784 | { 785 | "m_Id": "698f1e732a9d4a4ba2af85fb8f6d9dfc" 786 | } 787 | ], 788 | "synonyms": [ 789 | "texcoords", 790 | "coords", 791 | "coordinates" 792 | ], 793 | "m_Precision": 0, 794 | "m_PreviewExpanded": false, 795 | "m_PreviewMode": 0, 796 | "m_CustomColors": { 797 | "m_SerializableColors": [] 798 | }, 799 | "m_OutputChannel": 0 800 | } 801 | 802 | { 803 | "m_SGVersion": 1, 804 | "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", 805 | "m_ObjectId": "b74f60ff52314eaf9d500f61acca891d", 806 | "m_Guid": { 807 | "m_GuidSerialized": "4fefd862-66e8-4ef1-bf1d-544b20b77d96" 808 | }, 809 | "m_Name": "Alpha", 810 | "m_DefaultRefNameVersion": 1, 811 | "m_RefNameGeneratedByDisplayName": "Alpha", 812 | "m_DefaultReferenceName": "_Alpha", 813 | "m_OverrideReferenceName": "", 814 | "m_GeneratePropertyBlock": true, 815 | "m_UseCustomSlotLabel": false, 816 | "m_CustomSlotLabel": "", 817 | "m_Precision": 0, 818 | "overrideHLSLDeclaration": false, 819 | "hlslDeclarationOverride": 0, 820 | "m_Hidden": false, 821 | "m_Value": 1.0, 822 | "m_FloatType": 0, 823 | "m_RangeValues": { 824 | "x": 0.0, 825 | "y": 1.0 826 | } 827 | } 828 | 829 | { 830 | "m_SGVersion": 0, 831 | "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", 832 | "m_ObjectId": "bebd62eebdb1471da638e542d2772058", 833 | "m_Id": 1, 834 | "m_DisplayName": "Alpha", 835 | "m_SlotType": 0, 836 | "m_Hidden": false, 837 | "m_ShaderOutputName": "Alpha", 838 | "m_StageCapability": 3, 839 | "m_Value": 0.0, 840 | "m_DefaultValue": 0.0, 841 | "m_Labels": [] 842 | } 843 | 844 | { 845 | "m_SGVersion": 0, 846 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 847 | "m_ObjectId": "c1c025b47abe498cadd08fefb4e4236f", 848 | "m_Id": 2, 849 | "m_DisplayName": "Out", 850 | "m_SlotType": 1, 851 | "m_Hidden": false, 852 | "m_ShaderOutputName": "Out", 853 | "m_StageCapability": 3, 854 | "m_Value": { 855 | "e00": 0.0, 856 | "e01": 0.0, 857 | "e02": 0.0, 858 | "e03": 0.0, 859 | "e10": 0.0, 860 | "e11": 0.0, 861 | "e12": 0.0, 862 | "e13": 0.0, 863 | "e20": 0.0, 864 | "e21": 0.0, 865 | "e22": 0.0, 866 | "e23": 0.0, 867 | "e30": 0.0, 868 | "e31": 0.0, 869 | "e32": 0.0, 870 | "e33": 0.0 871 | }, 872 | "m_DefaultValue": { 873 | "e00": 1.0, 874 | "e01": 0.0, 875 | "e02": 0.0, 876 | "e03": 0.0, 877 | "e10": 0.0, 878 | "e11": 1.0, 879 | "e12": 0.0, 880 | "e13": 0.0, 881 | "e20": 0.0, 882 | "e21": 0.0, 883 | "e22": 1.0, 884 | "e23": 0.0, 885 | "e30": 0.0, 886 | "e31": 0.0, 887 | "e32": 0.0, 888 | "e33": 1.0 889 | } 890 | } 891 | 892 | { 893 | "m_SGVersion": 0, 894 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 895 | "m_ObjectId": "ceaa2f3025424970991f1923d1e82c05", 896 | "m_Id": 2, 897 | "m_DisplayName": "Out", 898 | "m_SlotType": 1, 899 | "m_Hidden": false, 900 | "m_ShaderOutputName": "Out", 901 | "m_StageCapability": 3, 902 | "m_Value": { 903 | "e00": 0.0, 904 | "e01": 0.0, 905 | "e02": 0.0, 906 | "e03": 0.0, 907 | "e10": 0.0, 908 | "e11": 0.0, 909 | "e12": 0.0, 910 | "e13": 0.0, 911 | "e20": 0.0, 912 | "e21": 0.0, 913 | "e22": 0.0, 914 | "e23": 0.0, 915 | "e30": 0.0, 916 | "e31": 0.0, 917 | "e32": 0.0, 918 | "e33": 0.0 919 | }, 920 | "m_DefaultValue": { 921 | "e00": 1.0, 922 | "e01": 0.0, 923 | "e02": 0.0, 924 | "e03": 0.0, 925 | "e10": 0.0, 926 | "e11": 1.0, 927 | "e12": 0.0, 928 | "e13": 0.0, 929 | "e20": 0.0, 930 | "e21": 0.0, 931 | "e22": 1.0, 932 | "e23": 0.0, 933 | "e30": 0.0, 934 | "e31": 0.0, 935 | "e32": 0.0, 936 | "e33": 1.0 937 | } 938 | } 939 | 940 | { 941 | "m_SGVersion": 0, 942 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 943 | "m_ObjectId": "d3b61112276241ce829ae62b77cf259d", 944 | "m_Id": 1, 945 | "m_DisplayName": "B", 946 | "m_SlotType": 0, 947 | "m_Hidden": false, 948 | "m_ShaderOutputName": "B", 949 | "m_StageCapability": 3, 950 | "m_Value": { 951 | "e00": 2.0, 952 | "e01": 2.0, 953 | "e02": 2.0, 954 | "e03": 2.0, 955 | "e10": 2.0, 956 | "e11": 2.0, 957 | "e12": 2.0, 958 | "e13": 2.0, 959 | "e20": 2.0, 960 | "e21": 2.0, 961 | "e22": 2.0, 962 | "e23": 2.0, 963 | "e30": 2.0, 964 | "e31": 2.0, 965 | "e32": 2.0, 966 | "e33": 2.0 967 | }, 968 | "m_DefaultValue": { 969 | "e00": 1.0, 970 | "e01": 0.0, 971 | "e02": 0.0, 972 | "e03": 0.0, 973 | "e10": 0.0, 974 | "e11": 1.0, 975 | "e12": 0.0, 976 | "e13": 0.0, 977 | "e20": 0.0, 978 | "e21": 0.0, 979 | "e22": 1.0, 980 | "e23": 0.0, 981 | "e30": 0.0, 982 | "e31": 0.0, 983 | "e32": 0.0, 984 | "e33": 1.0 985 | } 986 | } 987 | 988 | { 989 | "m_SGVersion": 0, 990 | "m_Type": "UnityEditor.ShaderGraph.SubGraphOutputNode", 991 | "m_ObjectId": "dd2ba1bba9e54564a6ec2ac8b267fbd8", 992 | "m_Group": { 993 | "m_Id": "" 994 | }, 995 | "m_Name": "Output", 996 | "m_DrawState": { 997 | "m_Expanded": true, 998 | "m_Position": { 999 | "serializedVersion": "2", 1000 | "x": 516.7500610351563, 1001 | "y": 0.0, 1002 | "width": 0.0, 1003 | "height": 0.0 1004 | } 1005 | }, 1006 | "m_Slots": [ 1007 | { 1008 | "m_Id": "bebd62eebdb1471da638e542d2772058" 1009 | } 1010 | ], 1011 | "synonyms": [], 1012 | "m_Precision": 0, 1013 | "m_PreviewExpanded": true, 1014 | "m_PreviewMode": 0, 1015 | "m_CustomColors": { 1016 | "m_SerializableColors": [] 1017 | }, 1018 | "IsFirstSlotValid": true 1019 | } 1020 | 1021 | { 1022 | "m_SGVersion": 0, 1023 | "m_Type": "UnityEditor.ShaderGraph.DynamicValueMaterialSlot", 1024 | "m_ObjectId": "e1568262f8964046b25c1350762b5e93", 1025 | "m_Id": 2, 1026 | "m_DisplayName": "Out", 1027 | "m_SlotType": 1, 1028 | "m_Hidden": false, 1029 | "m_ShaderOutputName": "Out", 1030 | "m_StageCapability": 3, 1031 | "m_Value": { 1032 | "e00": 0.0, 1033 | "e01": 0.0, 1034 | "e02": 0.0, 1035 | "e03": 0.0, 1036 | "e10": 0.0, 1037 | "e11": 0.0, 1038 | "e12": 0.0, 1039 | "e13": 0.0, 1040 | "e20": 0.0, 1041 | "e21": 0.0, 1042 | "e22": 0.0, 1043 | "e23": 0.0, 1044 | "e30": 0.0, 1045 | "e31": 0.0, 1046 | "e32": 0.0, 1047 | "e33": 0.0 1048 | }, 1049 | "m_DefaultValue": { 1050 | "e00": 1.0, 1051 | "e01": 0.0, 1052 | "e02": 0.0, 1053 | "e03": 0.0, 1054 | "e10": 0.0, 1055 | "e11": 1.0, 1056 | "e12": 0.0, 1057 | "e13": 0.0, 1058 | "e20": 0.0, 1059 | "e21": 0.0, 1060 | "e22": 1.0, 1061 | "e23": 0.0, 1062 | "e30": 0.0, 1063 | "e31": 0.0, 1064 | "e32": 0.0, 1065 | "e33": 1.0 1066 | } 1067 | } 1068 | 1069 | { 1070 | "m_SGVersion": 0, 1071 | "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", 1072 | "m_ObjectId": "e3a7322b4e94408280104599e55afac6", 1073 | "m_Id": 4, 1074 | "m_DisplayName": "A", 1075 | "m_SlotType": 1, 1076 | "m_Hidden": false, 1077 | "m_ShaderOutputName": "A", 1078 | "m_StageCapability": 3, 1079 | "m_Value": 0.0, 1080 | "m_DefaultValue": 0.0, 1081 | "m_Labels": [] 1082 | } 1083 | 1084 | { 1085 | "m_SGVersion": 0, 1086 | "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", 1087 | "m_ObjectId": "e836becdc5364c7d8ddb4a8c279bccc9", 1088 | "m_Id": 0, 1089 | "m_DisplayName": "Alpha", 1090 | "m_SlotType": 1, 1091 | "m_Hidden": false, 1092 | "m_ShaderOutputName": "Out", 1093 | "m_StageCapability": 3, 1094 | "m_Value": 0.0, 1095 | "m_DefaultValue": 0.0, 1096 | "m_Labels": [] 1097 | } 1098 | 1099 | { 1100 | "m_SGVersion": 0, 1101 | "m_Type": "UnityEditor.ShaderGraph.SubtractNode", 1102 | "m_ObjectId": "f742d872c3644c91a7e7d0e40377ac4b", 1103 | "m_Group": { 1104 | "m_Id": "" 1105 | }, 1106 | "m_Name": "Subtract", 1107 | "m_DrawState": { 1108 | "m_Expanded": true, 1109 | "m_Position": { 1110 | "serializedVersion": "2", 1111 | "x": 139.0, 1112 | "y": 0.0, 1113 | "width": 126.0, 1114 | "height": 118.0 1115 | } 1116 | }, 1117 | "m_Slots": [ 1118 | { 1119 | "m_Id": "3fde0b7067d845198b86fe7096dc76bc" 1120 | }, 1121 | { 1122 | "m_Id": "462e0d8c5bca4d7bb89e85d1ca15c5ae" 1123 | }, 1124 | { 1125 | "m_Id": "5623051ea6094598a798d302a4fbca20" 1126 | } 1127 | ], 1128 | "synonyms": [ 1129 | "subtraction", 1130 | "remove", 1131 | "minus", 1132 | "take away" 1133 | ], 1134 | "m_Precision": 0, 1135 | "m_PreviewExpanded": false, 1136 | "m_PreviewMode": 0, 1137 | "m_CustomColors": { 1138 | "m_SerializableColors": [] 1139 | } 1140 | } 1141 | 1142 | { 1143 | "m_SGVersion": 0, 1144 | "m_Type": "UnityEditor.ShaderGraph.PropertyNode", 1145 | "m_ObjectId": "f9544d8c79c949e0985fd23aba80a7ae", 1146 | "m_Group": { 1147 | "m_Id": "" 1148 | }, 1149 | "m_Name": "Property", 1150 | "m_DrawState": { 1151 | "m_Expanded": true, 1152 | "m_Position": { 1153 | "serializedVersion": "2", 1154 | "x": 286.0, 1155 | "y": -34.0, 1156 | "width": 105.0, 1157 | "height": 34.0 1158 | } 1159 | }, 1160 | "m_Slots": [ 1161 | { 1162 | "m_Id": "e836becdc5364c7d8ddb4a8c279bccc9" 1163 | } 1164 | ], 1165 | "synonyms": [], 1166 | "m_Precision": 0, 1167 | "m_PreviewExpanded": true, 1168 | "m_PreviewMode": 0, 1169 | "m_CustomColors": { 1170 | "m_SerializableColors": [] 1171 | }, 1172 | "m_Property": { 1173 | "m_Id": "b74f60ff52314eaf9d500f61acca891d" 1174 | } 1175 | } 1176 | 1177 | { 1178 | "m_SGVersion": 0, 1179 | "m_Type": "UnityEditor.ShaderGraph.MultiplyNode", 1180 | "m_ObjectId": "faa4cd12c9814b6085e980b39a269ddd", 1181 | "m_Group": { 1182 | "m_Id": "" 1183 | }, 1184 | "m_Name": "Multiply", 1185 | "m_DrawState": { 1186 | "m_Expanded": true, 1187 | "m_Position": { 1188 | "serializedVersion": "2", 1189 | "x": 13.0, 1190 | "y": 0.0, 1191 | "width": 126.0, 1192 | "height": 118.0 1193 | } 1194 | }, 1195 | "m_Slots": [ 1196 | { 1197 | "m_Id": "b1bc7f5858ea4eb8ab5cd007c8f30439" 1198 | }, 1199 | { 1200 | "m_Id": "d3b61112276241ce829ae62b77cf259d" 1201 | }, 1202 | { 1203 | "m_Id": "e1568262f8964046b25c1350762b5e93" 1204 | } 1205 | ], 1206 | "synonyms": [ 1207 | "multiplication", 1208 | "times", 1209 | "x" 1210 | ], 1211 | "m_Precision": 0, 1212 | "m_PreviewExpanded": false, 1213 | "m_PreviewMode": 0, 1214 | "m_CustomColors": { 1215 | "m_SerializableColors": [] 1216 | } 1217 | } 1218 | 1219 | -------------------------------------------------------------------------------- /Shader/CubicBezier.shadersubgraph.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5851c76176f347c4e823f89e7146f4f2 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 11500000, guid: 60072b568d64c40a485e0fc55012dc9f, type: 3} 11 | -------------------------------------------------------------------------------- /Shader/CubicBezierCurve.shader: -------------------------------------------------------------------------------- 1 | Shader "Unlit/Cubic Bezier" 2 | { 3 | Properties 4 | { 5 | [HDR] _Color("Color", Color) = (0, 1, 1, 1) 6 | _StrokeThickness("Stroke Thickness", Float) = 10 7 | } 8 | SubShader 9 | { 10 | Tags { "RenderType"="Opaque" } 11 | LOD 100 12 | Cull Off 13 | 14 | Pass 15 | { 16 | CGPROGRAM 17 | #pragma vertex vert 18 | #pragma fragment frag 19 | 20 | #include "UnityCG.cginc" 21 | 22 | struct appdata 23 | { 24 | float4 vertex : POSITION; 25 | float3 uv : TEXCOORD0; 26 | }; 27 | 28 | struct v2f 29 | { 30 | float3 uv : TEXCOORD0; 31 | float4 vertex : SV_POSITION; 32 | }; 33 | 34 | v2f vert (appdata v) 35 | { 36 | v2f o; 37 | o.vertex = UnityObjectToClipPos(v.vertex); 38 | o.uv = v.uv; 39 | return o; 40 | } 41 | 42 | uniform half4 _Color; 43 | uniform float _StrokeThickness; 44 | 45 | half4 frag (v2f i) : SV_Target 46 | { 47 | float3 uv = i.uv; 48 | float alpha = uv.x * uv.x * uv.x - uv.y * uv.z; 49 | 50 | clip(alpha); 51 | return _Color; 52 | } 53 | ENDCG 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Shader/CubicBezierCurve.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b00b4fa244cbb114aa30764ae9f34fa6 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | preprocessorOverride: 0 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Shader/QuadraticBezier.shadersubgraph.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1d26a66ae7642f1428436d60439cd4f5 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 11500000, guid: 60072b568d64c40a485e0fc55012dc9f, type: 3} 11 | -------------------------------------------------------------------------------- /Shader/QuadraticBezierCurve.shader: -------------------------------------------------------------------------------- 1 | Shader "Unlit/Quadratic Bezier" 2 | { 3 | Properties 4 | { 5 | [HDR] _Color("Color", Color) = (0, 1, 1, 1) 6 | _StrokeThickness("Stroke Thickness", Float) = 10 7 | } 8 | SubShader 9 | { 10 | Tags { "RenderType"="Opaque" } 11 | LOD 100 12 | Cull Off 13 | 14 | Pass 15 | { 16 | CGPROGRAM 17 | #pragma vertex vert 18 | #pragma fragment frag 19 | 20 | #include "UnityCG.cginc" 21 | 22 | struct appdata 23 | { 24 | float4 vertex : POSITION; 25 | float3 uv : TEXCOORD0; 26 | }; 27 | 28 | struct v2f 29 | { 30 | float3 uv : TEXCOORD0; 31 | float4 vertex : SV_POSITION; 32 | }; 33 | 34 | v2f vert (appdata v) 35 | { 36 | v2f o; 37 | o.vertex = UnityObjectToClipPos(v.vertex); 38 | o.uv = v.uv; 39 | return o; 40 | } 41 | 42 | uniform half4 _Color; 43 | uniform float _StrokeThickness; 44 | 45 | half4 frag (v2f i) : SV_Target 46 | { 47 | float3 uv = i.uv; 48 | float alpha = (uv.x * uv.x - uv.y) * uv.z; 49 | 50 | clip(alpha); 51 | return _Color; 52 | } 53 | ENDCG 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Shader/QuadraticBezierCurve.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9f0a860722379f9479b1c3bfdea3dc76 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | preprocessorOverride: 0 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Shader/SimpleLitQuadraticBezier.shadergraph.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1a1e6f47ca0ec4b43a804d8194ed560b 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 11500000, guid: 625f186215c104763be7675aa2d941aa, type: 3} 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voxell.vectorgraphics", 3 | "displayName": "VX GPU Vector Graphics", 4 | "author": "Nixon", 5 | "description": "Generating vector graphics using the power of GPU.", 6 | "keywords": [ 7 | "vector", 8 | "graphics", 9 | "voxell" 10 | ], 11 | "license": "Apache 2.0", 12 | "unity": "2020.4", 13 | "unityRelease": "0f1", 14 | "version": "0.3.3-alpha", 15 | "dependencies": { 16 | "com.unity.mathematics": "1.2.5", 17 | "com.unity.collections": "1.2.3", 18 | "com.unity.jobs": "0.11.0-preview.6", 19 | "com.unity.burst": "1.6.5", 20 | "voxell.util": "1.4.0" 21 | }, 22 | "hideInEditor": false 23 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 636b3ed634303e248903415943533c89 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------