├── .gitignore ├── .vs └── UnityVectorEditor │ └── xs │ └── sqlite3 │ └── storage.ide ├── Assembly-CSharp-Editor.csproj ├── Assembly-CSharp.csproj ├── Assets ├── Scenes.meta ├── Scenes │ ├── SampleScene.unity │ ├── SampleScene.unity.meta │ ├── SampleShape.asset │ ├── SampleShape.asset.meta │ ├── SampleShapeUser.cs │ └── SampleShapeUser.cs.meta ├── VectorShapes.meta └── VectorShapes │ ├── CircleShape.cs │ ├── CircleShape.cs.meta │ ├── CompoundShape.cs │ ├── CompoundShape.cs.meta │ ├── Editor.meta │ ├── Editor │ ├── SerializedShapeDrawer.cs │ ├── SerializedShapeDrawer.cs.meta │ ├── SingleSelectionPopup.cs │ ├── SingleSelectionPopup.cs.meta │ ├── VectorShapeEditor.cs │ └── VectorShapeEditor.cs.meta │ ├── EllipseShape.cs │ ├── EllipseShape.cs.meta │ ├── PointShape.cs │ ├── PointShape.cs.meta │ ├── PolyShape.cs │ ├── PolyShape.cs.meta │ ├── SerializedShape.cs │ ├── SerializedShape.cs.meta │ ├── Shader.meta │ ├── Shader │ ├── VectorLineMeshBuilder.cs │ ├── VectorLineMeshBuilder.cs.meta │ ├── VectorLineShader.shader │ └── VectorLineShader.shader.meta │ ├── TextShape.cs │ ├── TextShape.cs.meta │ ├── VectorShape.cs │ ├── VectorShape.cs.meta │ ├── VectorShapeFIlesDXF.cs │ ├── VectorShapeFIlesDXF.cs.meta │ ├── VectorShapeFilesSVG.cs │ ├── VectorShapeFilesSVG.cs.meta │ ├── VectorShapeIcons.cs │ ├── VectorShapeIcons.cs.meta │ ├── VectorShapeUtils.cs │ └── VectorShapeUtils.cs.meta ├── LICENSE ├── PREVIEW.png ├── Packages └── manifest.json ├── ProjectSettings └── ProjectSettings.asset ├── README.md └── UnityVectorEditor.sln /.gitignore: -------------------------------------------------------------------------------- 1 | Library 2 | Temp 3 | ProjectSettings 4 | -------------------------------------------------------------------------------- /.vs/UnityVectorEditor/xs/sqlite3/storage.ide: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecurtz/UnityVectorEditor/e858f29b975dc0eb15a20bae77e3cfdc68c188b9/.vs/UnityVectorEditor/xs/sqlite3/storage.ide -------------------------------------------------------------------------------- /Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e0f3c76785d7a43478e5e7eb6f82bce9 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scenes/SampleScene.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 170076734} 41 | m_IndirectSpecularColor: {r: 0.44657838, g: 0.49641234, b: 0.57481676, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &3 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 11 47 | m_GIWorkflowMode: 0 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_EnvironmentLightingMode: 0 54 | m_EnableBakedLightmaps: 1 55 | m_EnableRealtimeLightmaps: 0 56 | m_LightmapEditorSettings: 57 | serializedVersion: 10 58 | m_Resolution: 2 59 | m_BakeResolution: 10 60 | m_AtlasSize: 512 61 | m_AO: 0 62 | m_AOMaxDistance: 1 63 | m_CompAOExponent: 1 64 | m_CompAOExponentDirect: 0 65 | m_Padding: 2 66 | m_LightmapParameters: {fileID: 0} 67 | m_LightmapsBakeMode: 1 68 | m_TextureCompression: 1 69 | m_FinalGather: 0 70 | m_FinalGatherFiltering: 1 71 | m_FinalGatherRayCount: 256 72 | m_ReflectionCompression: 2 73 | m_MixedBakeMode: 2 74 | m_BakeBackend: 1 75 | m_PVRSampling: 1 76 | m_PVRDirectSampleCount: 32 77 | m_PVRSampleCount: 256 78 | m_PVRBounces: 2 79 | m_PVRFilterTypeDirect: 0 80 | m_PVRFilterTypeIndirect: 0 81 | m_PVRFilterTypeAO: 0 82 | m_PVRFilteringMode: 1 83 | m_PVRCulling: 1 84 | m_PVRFilteringGaussRadiusDirect: 1 85 | m_PVRFilteringGaussRadiusIndirect: 5 86 | m_PVRFilteringGaussRadiusAO: 2 87 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 88 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 89 | m_PVRFilteringAtrousPositionSigmaAO: 1 90 | m_ShowResolutionOverlay: 1 91 | m_LightingDataAsset: {fileID: 0} 92 | m_UseShadowmask: 1 93 | --- !u!196 &4 94 | NavMeshSettings: 95 | serializedVersion: 2 96 | m_ObjectHideFlags: 0 97 | m_BuildSettings: 98 | serializedVersion: 2 99 | agentTypeID: 0 100 | agentRadius: 0.5 101 | agentHeight: 2 102 | agentSlope: 45 103 | agentClimb: 0.4 104 | ledgeDropHeight: 0 105 | maxJumpAcrossDistance: 0 106 | minRegionArea: 2 107 | manualCellSize: 0 108 | cellSize: 0.16666667 109 | manualTileSize: 0 110 | tileSize: 256 111 | accuratePlacement: 0 112 | debug: 113 | m_Flags: 0 114 | m_NavMeshData: {fileID: 0} 115 | --- !u!1 &170076733 116 | GameObject: 117 | m_ObjectHideFlags: 0 118 | m_CorrespondingSourceObject: {fileID: 0} 119 | m_PrefabInstance: {fileID: 0} 120 | m_PrefabAsset: {fileID: 0} 121 | serializedVersion: 6 122 | m_Component: 123 | - component: {fileID: 170076735} 124 | - component: {fileID: 170076734} 125 | m_Layer: 0 126 | m_Name: Directional Light 127 | m_TagString: Untagged 128 | m_Icon: {fileID: 0} 129 | m_NavMeshLayer: 0 130 | m_StaticEditorFlags: 0 131 | m_IsActive: 1 132 | --- !u!108 &170076734 133 | Light: 134 | m_ObjectHideFlags: 0 135 | m_CorrespondingSourceObject: {fileID: 0} 136 | m_PrefabInstance: {fileID: 0} 137 | m_PrefabAsset: {fileID: 0} 138 | m_GameObject: {fileID: 170076733} 139 | m_Enabled: 1 140 | serializedVersion: 8 141 | m_Type: 1 142 | m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} 143 | m_Intensity: 1 144 | m_Range: 10 145 | m_SpotAngle: 30 146 | m_CookieSize: 10 147 | m_Shadows: 148 | m_Type: 2 149 | m_Resolution: -1 150 | m_CustomResolution: -1 151 | m_Strength: 1 152 | m_Bias: 0.05 153 | m_NormalBias: 0.4 154 | m_NearPlane: 0.2 155 | m_Cookie: {fileID: 0} 156 | m_DrawHalo: 0 157 | m_Flare: {fileID: 0} 158 | m_RenderMode: 0 159 | m_CullingMask: 160 | serializedVersion: 2 161 | m_Bits: 4294967295 162 | m_Lightmapping: 1 163 | m_LightShadowCasterMode: 0 164 | m_AreaSize: {x: 1, y: 1} 165 | m_BounceIntensity: 1 166 | m_ColorTemperature: 6570 167 | m_UseColorTemperature: 0 168 | m_ShadowRadius: 0 169 | m_ShadowAngle: 0 170 | --- !u!4 &170076735 171 | Transform: 172 | m_ObjectHideFlags: 0 173 | m_CorrespondingSourceObject: {fileID: 0} 174 | m_PrefabInstance: {fileID: 0} 175 | m_PrefabAsset: {fileID: 0} 176 | m_GameObject: {fileID: 170076733} 177 | m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} 178 | m_LocalPosition: {x: 0, y: 3, z: 0} 179 | m_LocalScale: {x: 1, y: 1, z: 1} 180 | m_Children: [] 181 | m_Father: {fileID: 0} 182 | m_RootOrder: 1 183 | m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} 184 | --- !u!1 &534669902 185 | GameObject: 186 | m_ObjectHideFlags: 0 187 | m_CorrespondingSourceObject: {fileID: 0} 188 | m_PrefabInstance: {fileID: 0} 189 | m_PrefabAsset: {fileID: 0} 190 | serializedVersion: 6 191 | m_Component: 192 | - component: {fileID: 534669905} 193 | - component: {fileID: 534669904} 194 | - component: {fileID: 534669903} 195 | m_Layer: 0 196 | m_Name: Main Camera 197 | m_TagString: MainCamera 198 | m_Icon: {fileID: 0} 199 | m_NavMeshLayer: 0 200 | m_StaticEditorFlags: 0 201 | m_IsActive: 1 202 | --- !u!81 &534669903 203 | AudioListener: 204 | m_ObjectHideFlags: 0 205 | m_CorrespondingSourceObject: {fileID: 0} 206 | m_PrefabInstance: {fileID: 0} 207 | m_PrefabAsset: {fileID: 0} 208 | m_GameObject: {fileID: 534669902} 209 | m_Enabled: 1 210 | --- !u!20 &534669904 211 | Camera: 212 | m_ObjectHideFlags: 0 213 | m_CorrespondingSourceObject: {fileID: 0} 214 | m_PrefabInstance: {fileID: 0} 215 | m_PrefabAsset: {fileID: 0} 216 | m_GameObject: {fileID: 534669902} 217 | m_Enabled: 1 218 | serializedVersion: 2 219 | m_ClearFlags: 1 220 | m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} 221 | m_projectionMatrixMode: 1 222 | m_SensorSize: {x: 36, y: 24} 223 | m_LensShift: {x: 0, y: 0} 224 | m_GateFitMode: 2 225 | m_FocalLength: 50 226 | m_NormalizedViewPortRect: 227 | serializedVersion: 2 228 | x: 0 229 | y: 0 230 | width: 1 231 | height: 1 232 | near clip plane: 0.3 233 | far clip plane: 1000 234 | field of view: 60 235 | orthographic: 0 236 | orthographic size: 5 237 | m_Depth: -1 238 | m_CullingMask: 239 | serializedVersion: 2 240 | m_Bits: 4294967295 241 | m_RenderingPath: -1 242 | m_TargetTexture: {fileID: 0} 243 | m_TargetDisplay: 0 244 | m_TargetEye: 3 245 | m_HDR: 1 246 | m_AllowMSAA: 1 247 | m_AllowDynamicResolution: 0 248 | m_ForceIntoRT: 0 249 | m_OcclusionCulling: 1 250 | m_StereoConvergence: 10 251 | m_StereoSeparation: 0.022 252 | --- !u!4 &534669905 253 | Transform: 254 | m_ObjectHideFlags: 0 255 | m_CorrespondingSourceObject: {fileID: 0} 256 | m_PrefabInstance: {fileID: 0} 257 | m_PrefabAsset: {fileID: 0} 258 | m_GameObject: {fileID: 534669902} 259 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 260 | m_LocalPosition: {x: 0, y: 1, z: -10} 261 | m_LocalScale: {x: 1, y: 1, z: 1} 262 | m_Children: [] 263 | m_Father: {fileID: 0} 264 | m_RootOrder: 0 265 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 266 | --- !u!1 &1632254178 267 | GameObject: 268 | m_ObjectHideFlags: 0 269 | m_CorrespondingSourceObject: {fileID: 0} 270 | m_PrefabInstance: {fileID: 0} 271 | m_PrefabAsset: {fileID: 0} 272 | serializedVersion: 6 273 | m_Component: 274 | - component: {fileID: 1632254180} 275 | - component: {fileID: 1632254179} 276 | m_Layer: 0 277 | m_Name: Icon Render Camera 278 | m_TagString: Untagged 279 | m_Icon: {fileID: 0} 280 | m_NavMeshLayer: 0 281 | m_StaticEditorFlags: 0 282 | m_IsActive: 1 283 | --- !u!20 &1632254179 284 | Camera: 285 | m_ObjectHideFlags: 0 286 | m_CorrespondingSourceObject: {fileID: 0} 287 | m_PrefabInstance: {fileID: 0} 288 | m_PrefabAsset: {fileID: 0} 289 | m_GameObject: {fileID: 1632254178} 290 | m_Enabled: 0 291 | serializedVersion: 2 292 | m_ClearFlags: 2 293 | m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0} 294 | m_projectionMatrixMode: 1 295 | m_SensorSize: {x: 36, y: 24} 296 | m_LensShift: {x: 0, y: 0} 297 | m_GateFitMode: 2 298 | m_FocalLength: 50 299 | m_NormalizedViewPortRect: 300 | serializedVersion: 2 301 | x: 0 302 | y: 0 303 | width: 1 304 | height: 1 305 | near clip plane: 0.1 306 | far clip plane: 100 307 | field of view: 60 308 | orthographic: 1 309 | orthographic size: 12 310 | m_Depth: 0 311 | m_CullingMask: 312 | serializedVersion: 2 313 | m_Bits: 4294967295 314 | m_RenderingPath: -1 315 | m_TargetTexture: {fileID: 0} 316 | m_TargetDisplay: 0 317 | m_TargetEye: 3 318 | m_HDR: 1 319 | m_AllowMSAA: 1 320 | m_AllowDynamicResolution: 0 321 | m_ForceIntoRT: 0 322 | m_OcclusionCulling: 1 323 | m_StereoConvergence: 10 324 | m_StereoSeparation: 0.022 325 | --- !u!4 &1632254180 326 | Transform: 327 | m_ObjectHideFlags: 0 328 | m_CorrespondingSourceObject: {fileID: 0} 329 | m_PrefabInstance: {fileID: 0} 330 | m_PrefabAsset: {fileID: 0} 331 | m_GameObject: {fileID: 1632254178} 332 | m_LocalRotation: {x: 0, y: 0, z: 0.000002972375, w: 1} 333 | m_LocalPosition: {x: 12, y: 12, z: -1} 334 | m_LocalScale: {x: 1, y: 1, z: 1} 335 | m_Children: [] 336 | m_Father: {fileID: 0} 337 | m_RootOrder: 3 338 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 339 | --- !u!1 &1661268765 340 | GameObject: 341 | m_ObjectHideFlags: 0 342 | m_CorrespondingSourceObject: {fileID: 0} 343 | m_PrefabInstance: {fileID: 0} 344 | m_PrefabAsset: {fileID: 0} 345 | serializedVersion: 6 346 | m_Component: 347 | - component: {fileID: 1661268767} 348 | - component: {fileID: 1661268766} 349 | m_Layer: 0 350 | m_Name: ShapeUsingGameObject 351 | m_TagString: Untagged 352 | m_Icon: {fileID: 0} 353 | m_NavMeshLayer: 0 354 | m_StaticEditorFlags: 0 355 | m_IsActive: 1 356 | --- !u!114 &1661268766 357 | MonoBehaviour: 358 | m_ObjectHideFlags: 0 359 | m_CorrespondingSourceObject: {fileID: 0} 360 | m_PrefabInstance: {fileID: 0} 361 | m_PrefabAsset: {fileID: 0} 362 | m_GameObject: {fileID: 1661268765} 363 | m_Enabled: 1 364 | m_EditorHideFlags: 0 365 | m_Script: {fileID: 11500000, guid: 2ada356d7b2f741cdb2765daed2dfdd2, type: 3} 366 | m_Name: 367 | m_EditorClassIdentifier: 368 | shape: {fileID: 11400000, guid: 790bc1fea4add46599cf4211829edb90, type: 2} 369 | --- !u!4 &1661268767 370 | Transform: 371 | m_ObjectHideFlags: 0 372 | m_CorrespondingSourceObject: {fileID: 0} 373 | m_PrefabInstance: {fileID: 0} 374 | m_PrefabAsset: {fileID: 0} 375 | m_GameObject: {fileID: 1661268765} 376 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 377 | m_LocalPosition: {x: 0, y: 0, z: 0} 378 | m_LocalScale: {x: 1, y: 1, z: 1} 379 | m_Children: [] 380 | m_Father: {fileID: 0} 381 | m_RootOrder: 2 382 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 383 | -------------------------------------------------------------------------------- /Assets/Scenes/SampleScene.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d5b456b4e24004c8ba05933cefc759dc 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/Scenes/SampleShape.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &11400000 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 11500000, guid: c081e07c8a64f48c98e1c02e172f84f0, type: 3} 13 | m_Name: SampleShape 14 | m_EditorClassIdentifier: 15 | circles: 16 | - penSize: 2 17 | penToMeshScale: 0.23741485 18 | colorOutline: {r: 0, g: 0, b: 0, a: 1} 19 | colorFill: {r: 0.32156864, g: 0.8, b: 0.2627451, a: 1} 20 | closed: 1 21 | guid: 00000000-0000-0000-0000-000000000000 22 | position: {x: 14.267512, y: -14.140124} 23 | radius: 22.272238 24 | startAngle: 0 25 | sweepAngle: 0 26 | ellipses: [] 27 | points: [] 28 | polys: 29 | - penSize: 2 30 | penToMeshScale: 0.23741485 31 | colorOutline: {r: 0, g: 0, b: 0, a: 1} 32 | colorFill: {r: 0, g: 0, b: 0, a: 0} 33 | closed: 1 34 | guid: 00000000-0000-0000-0000-000000000000 35 | vertices: 36 | - position: {x: -20.891724, y: 56.17943} 37 | enterCP: {x: -20.896053, y: 26.119652} 38 | exitCP: {x: -20.887394, y: 26.119652} 39 | segmentCurves: 0 40 | - position: {x: 5.1430874, y: 11.078723} 41 | enterCP: {x: -20.88523, y: 26.115902} 42 | exitCP: {x: -20.889559, y: 26.108402} 43 | segmentCurves: 0 44 | - position: {x: -46.926537, y: 11.078723} 45 | enterCP: {x: -20.893888, y: 26.108402} 46 | exitCP: {x: -20.898218, y: 26.115902} 47 | segmentCurves: 0 48 | continuousCurves: 1 49 | activeIndex: -1 50 | activeComponent: 0 51 | - penSize: 2 52 | penToMeshScale: 0.23741485 53 | colorOutline: {r: 0.8784314, g: 0.5294118, b: 0.078431375, a: 1} 54 | colorFill: {r: 0, g: 0, b: 0, a: 0} 55 | closed: 1 56 | guid: 00000000-0000-0000-0000-000000000000 57 | vertices: 58 | - position: {x: -34.904465, y: 20.632269} 59 | enterCP: {x: -34.906963, y: -1.9058251} 60 | exitCP: {x: -34.901966, y: -1.9058251} 61 | segmentCurves: 0 62 | - position: {x: -15.382214, y: 9.3618355} 63 | enterCP: {x: -34.901386, y: -1.9061601} 64 | exitCP: {x: -34.898884, y: -1.9104902} 65 | segmentCurves: 0 66 | - position: {x: -15.38221, y: -13.183476} 67 | enterCP: {x: -34.898884, y: -1.9111601} 68 | exitCP: {x: -34.901386, y: -1.9154902} 69 | segmentCurves: 0 70 | - position: {x: -34.904465, y: -24.45392} 71 | enterCP: {x: -34.901966, y: -1.9158251} 72 | exitCP: {x: -34.906963, y: -1.9158251} 73 | segmentCurves: 0 74 | - position: {x: -54.42672, y: -13.183476} 75 | enterCP: {x: -34.907543, y: -1.9154902} 76 | exitCP: {x: -34.910046, y: -1.9111601} 77 | segmentCurves: 0 78 | - position: {x: -54.426716, y: 9.3618355} 79 | enterCP: {x: -34.910046, y: -1.9104902} 80 | exitCP: {x: -34.907543, y: -1.9061601} 81 | segmentCurves: 0 82 | continuousCurves: 1 83 | activeIndex: -1 84 | activeComponent: 0 85 | texts: [] 86 | -------------------------------------------------------------------------------- /Assets/Scenes/SampleShape.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 790bc1fea4add46599cf4211829edb90 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 11400000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scenes/SampleShapeUser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class SampleShapeUser : MonoBehaviour 6 | { 7 | public SerializedShape shape; 8 | 9 | void Update() 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Assets/Scenes/SampleShapeUser.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ada356d7b2f741cdb2765daed2dfdd2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0899a0484a8d54f03a5627e6feca6b57 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/VectorShapes/CircleShape.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml; 3 | using UnityEngine; 4 | #if UNITY_EDITOR 5 | using UnityEditor; 6 | #endif 7 | using Unity.VectorGraphics; 8 | 9 | [System.Serializable] 10 | /// 11 | /// Vector circle. 12 | /// 13 | public class CircleShape : VectorShape 14 | { 15 | public class CircleProxy : ScriptableObject, ISerializationCallbackReceiver 16 | { 17 | [HideInInspector] 18 | public CircleShape circle; 19 | 20 | public Vector2 position; 21 | public float radius; 22 | public float startAngle; 23 | public float sweepAngle; 24 | 25 | public void OnBeforeSerialize() 26 | { 27 | if (circle != null) 28 | { 29 | circle.position = position; 30 | circle.radius = radius; 31 | circle.startAngle = startAngle; 32 | circle.sweepAngle = sweepAngle; 33 | } 34 | } 35 | 36 | public void OnAfterDeserialize() 37 | { 38 | } 39 | } 40 | 41 | public CircleProxy GetCircleProxy() 42 | { 43 | CircleProxy proxy = ScriptableObject.CreateInstance(); 44 | proxy.name = "Circle"; 45 | proxy.circle = this; 46 | proxy.position = position; 47 | proxy.radius = radius; 48 | proxy.startAngle = startAngle; 49 | proxy.sweepAngle = sweepAngle; 50 | 51 | return proxy; 52 | } 53 | 54 | /// 55 | /// Position of center. 56 | /// 57 | [SerializeField] 58 | protected Vector2 position; 59 | 60 | /// 61 | /// Radius of circle. 62 | /// 63 | [SerializeField] 64 | protected float radius; 65 | 66 | /// 67 | /// Starting angle for arcs (in radians). 68 | /// 69 | [SerializeField] 70 | protected float startAngle; 71 | 72 | /// 73 | /// Sweep angle for arcs (in radians). 74 | /// 75 | [SerializeField] 76 | protected float sweepAngle; 77 | 78 | /// 79 | /// Position of center. 80 | /// 81 | public Vector2 Position 82 | { 83 | set 84 | { 85 | position = value; 86 | Dirty = true; 87 | } 88 | get 89 | { 90 | return position; 91 | } 92 | } 93 | 94 | /// 95 | /// Radius of circle. 96 | /// 97 | public float Radius 98 | { 99 | set 100 | { 101 | radius = value; 102 | Dirty = true; 103 | } 104 | get 105 | { 106 | return radius; 107 | } 108 | } 109 | 110 | /// 111 | /// Starting angle for circular arcs (in degrees). 112 | /// 113 | public float StartAngle 114 | { 115 | set 116 | { 117 | startAngle = value * Mathf.Deg2Rad; 118 | Dirty = true; 119 | } 120 | get 121 | { 122 | return startAngle * Mathf.Rad2Deg; 123 | } 124 | } 125 | 126 | /// 127 | /// Sweep angle for circular arcs (in degrees). 128 | /// 129 | public float SweepAngle 130 | { 131 | set 132 | { 133 | sweepAngle = value * Mathf.Deg2Rad; 134 | if (Mathf.Approximately(value, 0f) || (Mathf.Abs(value) >= 360f)) 135 | { 136 | closed = true; 137 | } 138 | else 139 | { 140 | closed = false; 141 | } 142 | Dirty = true; 143 | } 144 | get 145 | { 146 | return sweepAngle * Mathf.Rad2Deg; 147 | } 148 | } 149 | 150 | protected CircleShape() 151 | { 152 | } 153 | 154 | /// 155 | /// New circle from center point and radius. 156 | /// 157 | /// Center of circle 158 | /// Radius of circle 159 | public CircleShape(Vector2 center, float rad) 160 | { 161 | position = center; 162 | radius = rad; 163 | 164 | startAngle = 0f; 165 | sweepAngle = 0f; 166 | closed = true; 167 | } 168 | 169 | /// 170 | /// New circular arc from center point, radius, and angles. 171 | /// 172 | /// Center of circle 173 | /// Radius of circle 174 | /// Starting angle of arc (in degrees) 175 | /// Sweep of arc (in degrees) 176 | public CircleShape(Vector2 center, float rad, float angle, float sweep) 177 | { 178 | position = center; 179 | radius = rad; 180 | 181 | startAngle = angle * Mathf.Deg2Rad; 182 | sweepAngle = sweep * Mathf.Deg2Rad; 183 | 184 | if (Mathf.Approximately(sweep, 0f) || (Mathf.Abs(sweep) >= 360f)) 185 | { 186 | closed = true; 187 | } 188 | else 189 | { 190 | closed = false; 191 | } 192 | } 193 | 194 | public static CircleShape Create() 195 | { 196 | //CircleShape shape = ScriptableObject.CreateInstance(); 197 | CircleShape shape = new CircleShape(); 198 | 199 | return shape; 200 | } 201 | 202 | /// 203 | /// New circle from center point and radius. 204 | /// 205 | /// Center of circle 206 | /// Radius of circle 207 | public static CircleShape Create(Vector2 center, float rad) 208 | { 209 | CircleShape shape = Create(); 210 | 211 | shape.position = center; 212 | shape.radius = rad; 213 | 214 | shape.startAngle = 0f; 215 | shape.sweepAngle = 0f; 216 | shape.closed = true; 217 | 218 | return shape; 219 | } 220 | 221 | /// 222 | /// New circular arc from center point, radius, and angles. 223 | /// 224 | /// Center of circle 225 | /// Radius of circle 226 | /// Starting angle of arc (in degrees) 227 | /// Sweep of arc (in degrees) 228 | public static CircleShape Create(Vector2 center, float rad, float angle, float sweep) 229 | { 230 | CircleShape shape = Create(); 231 | 232 | shape.position = center; 233 | shape.radius = rad; 234 | 235 | shape.startAngle = angle * Mathf.Deg2Rad; 236 | shape.sweepAngle = sweep * Mathf.Deg2Rad; 237 | 238 | if (Mathf.Approximately(sweep, 0f) || (Mathf.Abs(sweep) >= 360f)) 239 | { 240 | shape.closed = true; 241 | } 242 | else 243 | { 244 | shape.closed = false; 245 | } 246 | 247 | return shape; 248 | } 249 | 250 | /// 251 | /// Copy of the shape. 252 | /// 253 | /// New shape with properties of existing shape 254 | public override VectorShape Duplicate() 255 | { 256 | return Create(position, radius, startAngle * Mathf.Rad2Deg, sweepAngle * Mathf.Rad2Deg); 257 | } 258 | 259 | /// 260 | /// Distance between a point and the shape. 261 | /// 262 | /// Test point 263 | /// Distance from point to nearest point on shape 264 | public override float Distance(Vector2 pt) 265 | { 266 | return Mathf.Abs(Vector2.Distance(pt, position) - radius); 267 | } 268 | 269 | /// 270 | /// Tests if a shape contains a point 271 | /// 272 | /// Test point 273 | /// Is the point inside the shape? 274 | public override bool Contains(Vector2 pt) 275 | { 276 | return (Vector2.Distance(pt, position) < radius); 277 | } 278 | 279 | /// 280 | /// Tests if a shape is inside a rectangle. 281 | /// 282 | /// Test rectangle 283 | /// Is the shape entirely inside the rectangle? 284 | public override bool IsInside(Rect rect) 285 | { 286 | if (!rect.Contains(position)) return false; 287 | 288 | Vector2 testPt = position; 289 | testPt.x = position.x - radius; 290 | if (!rect.Contains(testPt)) return false; 291 | testPt.x = position.x + radius; 292 | if (!rect.Contains(testPt)) return false; 293 | 294 | testPt = position; 295 | testPt.y = position.y - radius; 296 | if (!rect.Contains(testPt)) return false; 297 | testPt.y = position.y + radius; 298 | if (!rect.Contains(testPt)) return false; 299 | 300 | return true; 301 | } 302 | 303 | /// 304 | /// Rotate the shape around a point. 305 | /// 306 | /// Center of rotation 307 | /// Angle in degrees 308 | public override void RotateAround(Vector2 center, float angle) 309 | { 310 | Matrix2D matrix = Matrix2D.Translate(center) * Matrix2D.RotateRH(angle * Mathf.Deg2Rad) * Matrix2D.Translate(-center); 311 | position = matrix.MultiplyPoint(position); 312 | 313 | Dirty = true; 314 | } 315 | 316 | /// 317 | /// Change the origin of the shape. 318 | /// 319 | /// Direction to move 320 | public override void TranslateBy(Vector2 offset) 321 | { 322 | position += offset; 323 | 324 | Dirty = true; 325 | } 326 | 327 | /// 328 | /// Change the size of the shape. 329 | /// 330 | /// Scaling factor to apply 331 | public override void ScaleBy(float scale) 332 | { 333 | if (scale < Mathf.Epsilon) 334 | { 335 | Debug.LogWarning("Scale must be greater than zero."); 336 | return; 337 | } 338 | 339 | position *= scale; 340 | radius *= scale; 341 | 342 | Dirty = true; 343 | } 344 | 345 | /// 346 | /// Transform the shape by an arbitrary matrix. 347 | /// 348 | /// Matrix to transform shape 349 | public override void TransformBy(Matrix2D matrix) 350 | { 351 | // Attempt to identify uniform scaling 352 | Vector2 pt0 = matrix.MultiplyPoint(position + new Vector2(0, radius)); 353 | Vector2 pt1 = matrix.MultiplyPoint(position + new Vector2(0, -radius)); 354 | Vector2 pt2 = matrix.MultiplyPoint(position + new Vector2(radius, 0)); 355 | Vector2 pt3 = matrix.MultiplyPoint(position + new Vector2(-radius, 0)); 356 | 357 | position = matrix.MultiplyPoint(position); 358 | 359 | float distSqr = Vector2.SqrMagnitude(pt0 - position); 360 | if (Mathf.Approximately(distSqr, Vector2.SqrMagnitude(pt1 - position)) && 361 | Mathf.Approximately(distSqr, Vector2.SqrMagnitude(pt2 - position)) && 362 | Mathf.Approximately(distSqr, Vector2.SqrMagnitude(pt3 - position))) 363 | { 364 | radius = Mathf.Sqrt(distSqr); 365 | } 366 | else 367 | { 368 | Debug.LogWarning("Ignored matrix change that would destroy circle."); 369 | } 370 | 371 | Dirty = true; 372 | } 373 | 374 | /// 375 | /// Distance between a point and the shape. 376 | /// 377 | /// Test point 378 | /// Snap modes to consider 379 | /// Distance from point to nearest point on shape 380 | public override SnapPoint GetSnap(Vector2 pt, SnapPoint.Mode mode) 381 | { 382 | SnapPoint snap = new SnapPoint(); 383 | float distance = float.MaxValue; 384 | 385 | if ((mode & SnapPoint.Mode.Center) != 0) 386 | { 387 | float d = Vector2.Distance(pt, position); 388 | if (d < distance) 389 | { 390 | distance = d; 391 | snap.mode = SnapPoint.Mode.Center; 392 | snap.point = position; 393 | } 394 | } 395 | 396 | if ((mode & SnapPoint.Mode.Midpoint) != 0) 397 | { 398 | float offset45 = 0.7071f * radius; 399 | Vector2[] midPoints = 400 | { 401 | position + new Vector2(radius, 0f), 402 | position + new Vector2(offset45, offset45), 403 | position + new Vector2(0f, radius), 404 | position + new Vector2(-offset45, offset45), 405 | position + new Vector2(-radius, 0f), 406 | position + new Vector2(-offset45, -offset45), 407 | position + new Vector2(0f, -radius), 408 | position + new Vector2(offset45, -offset45), 409 | }; 410 | 411 | foreach (Vector2 testPt in midPoints) 412 | { 413 | float d = Vector2.Distance(pt, testPt); 414 | if (d < distance) 415 | { 416 | distance = d; 417 | snap.mode = SnapPoint.Mode.Midpoint; 418 | snap.point = testPt; 419 | } 420 | } 421 | } 422 | 423 | if ((mode & SnapPoint.Mode.Edge) != 0) 424 | { 425 | float d = Distance(pt); 426 | if (d < distance) 427 | { 428 | distance = d; 429 | snap.mode = SnapPoint.Mode.Edge; 430 | float angle = Mathf.Atan2(pt.y - position.y, pt.x - position.x); 431 | snap.point.x = position.x + Mathf.Cos(angle) * radius; 432 | snap.point.y = position.y + Mathf.Sin(angle) * radius; 433 | } 434 | } 435 | 436 | return snap; 437 | } 438 | 439 | /// 440 | /// Tessellate the shape into geometry data. 441 | /// 442 | protected override void GenerateGeometry() 443 | { 444 | if ((shapeGeometry != null) && (!shapeDirty)) return; 445 | 446 | Shape circle = new Shape(); 447 | if (closed) 448 | { 449 | VectorUtils.MakeCircleShape(circle, position, radius); 450 | } 451 | else 452 | { 453 | BezierContour contour = new BezierContour(); 454 | contour.Segments = VectorUtils.MakeArc(position, startAngle, sweepAngle, radius); 455 | circle.Contours = new BezierContour[] { contour }; 456 | } 457 | 458 | circle.PathProps = new PathProperties() 459 | { 460 | Stroke = new Stroke() 461 | { 462 | Color = colorOutline, 463 | HalfThickness = penSize / 2f * penToMeshScale 464 | } 465 | }; 466 | if (colorFill != Color.clear) 467 | { 468 | circle.Fill = new SolidFill() 469 | { 470 | Color = colorFill 471 | }; 472 | } 473 | 474 | shapeNode = new SceneNode() 475 | { 476 | Transform = matrixTransform, 477 | Shapes = new List 478 | { 479 | circle 480 | } 481 | }; 482 | 483 | tessellationScene.Root = shapeNode; 484 | shapeGeometry = VectorUtils.TessellateScene(tessellationScene, tessellationOptions); 485 | 486 | shapeDirty = false; 487 | } 488 | 489 | /// 490 | /// Build a mesh for display with the VectorLineShader. 491 | /// 492 | protected override void GenerateLineMesh() 493 | { 494 | if (closed) 495 | { 496 | lineBuilder.Circle(position, radius, 32); 497 | } 498 | else 499 | { 500 | BezierPathSegment[] segments = VectorUtils.MakeArc(position, startAngle, sweepAngle, radius); 501 | 502 | lineBuilder.BeginPolyLine(segments[0].P0); 503 | int steps = Mathf.CeilToInt(16f * sweepAngle / Mathf.PI / segments.Length); 504 | for (int i = 1; i < segments.Length; i++) 505 | { 506 | lineBuilder.CurveTo(segments[i - 1].P1, segments[i - 1].P2, segments[i].P0, steps); 507 | } 508 | lineBuilder.EndPolyLine(); 509 | } 510 | } 511 | 512 | /// 513 | /// Build a 2D bounding box for the shape. 514 | /// 515 | protected override void GenerateBounds() 516 | { 517 | shapeBounds = new Rect(position - new Vector2(radius, radius), new Vector2(radius * 2, radius * 2)); 518 | boundsDirty = false; 519 | } 520 | 521 | /// 522 | /// Build a 2D collider for the shape. 523 | /// 524 | protected override void AddColliderToGO(GameObject target) 525 | { 526 | CircleCollider2D[] colliders = target.GetComponents(); 527 | CircleCollider2D collider = null; 528 | 529 | for (int i = 0; i < colliders.Length; i++) 530 | { 531 | if (colliders[i].name == this.guid) 532 | { 533 | collider = colliders[i]; 534 | } 535 | } 536 | 537 | if (collider == null) 538 | { 539 | collider = collider.gameObject.AddComponent(); 540 | collider.name = this.guid; 541 | } 542 | 543 | collider.offset = position; 544 | collider.radius = radius; 545 | } 546 | 547 | /// 548 | /// Serialize the shape to an XML writer. 549 | /// 550 | public override void WriteToXML(XmlWriter writer, Vector2 origin, float scale) 551 | { 552 | Vector2 svgPosition = (position - origin) * new Vector2(scale, -scale); 553 | 554 | writer.WriteStartElement("circle"); 555 | 556 | writer.WriteStartAttribute("cx"); 557 | writer.WriteValue(svgPosition.x); 558 | writer.WriteEndAttribute(); 559 | 560 | writer.WriteStartAttribute("cy"); 561 | writer.WriteValue(svgPosition.y); 562 | writer.WriteEndAttribute(); 563 | 564 | writer.WriteStartAttribute("r"); 565 | writer.WriteValue(radius * scale); 566 | writer.WriteEndAttribute(); 567 | 568 | writer.WriteStartAttribute("stroke"); 569 | writer.WriteValue(VectorShapeFilesSVG.ConvertColor(colorOutline)); 570 | writer.WriteEndAttribute(); 571 | 572 | writer.WriteStartAttribute("stroke-width"); 573 | writer.WriteValue("1mm"); 574 | writer.WriteEndAttribute(); 575 | 576 | writer.WriteStartAttribute("fill"); 577 | writer.WriteValue(VectorShapeFilesSVG.ConvertColor(colorFill)); 578 | writer.WriteEndAttribute(); 579 | 580 | writer.WriteEndElement(); 581 | } 582 | 583 | #if UNITY_EDITOR 584 | /// 585 | /// Draw the circle to the active camera using editor handles. 586 | /// 587 | /// Is the shape selected? 588 | /// Is it the active shape? 589 | public override void DrawEditorHandles(bool selected, bool active = false) 590 | { 591 | base.DrawEditorHandles(selected, active); 592 | 593 | if (selected) 594 | { 595 | if (boundsDirty) GenerateBounds(); 596 | Handles.DrawSolidRectangleWithOutline(shapeBounds, Color.clear, Handles.color); 597 | } 598 | 599 | if (active) 600 | { 601 | 602 | } 603 | } 604 | 605 | /// 606 | /// Respond to GUI input events in editor. 607 | /// 608 | /// The current event 609 | /// Is it the active shape? 610 | /// Did the shape handle the event? 611 | public override bool HandleEditorEvent(Event currEvent, bool active) 612 | { 613 | return false; 614 | } 615 | #endif 616 | 617 | } -------------------------------------------------------------------------------- /Assets/VectorShapes/CircleShape.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1757820fbde764527be1d024c1d65efa 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/CompoundShape.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml; 3 | using UnityEngine; 4 | #if UNITY_EDITOR 5 | using UnityEditor; 6 | #endif 7 | using Unity.VectorGraphics; 8 | 9 | [System.Serializable] 10 | /// 11 | /// Vector shape composed of other shapes. 12 | /// 13 | public class CompoundShape : VectorShape 14 | { 15 | private List _components; 16 | public List components { get {return _components;} } 17 | 18 | /// 19 | /// New empty compound shape. 20 | /// 21 | public CompoundShape() 22 | { 23 | _components = new List(); 24 | } 25 | 26 | /// 27 | /// New empty compound shape. 28 | /// 29 | public static CompoundShape Create() 30 | { 31 | //CompoundShape shape = ScriptableObject.CreateInstance(); 32 | CompoundShape shape = new CompoundShape(); 33 | 34 | shape._components = new List(); 35 | 36 | return shape; 37 | } 38 | 39 | /// 40 | /// Copy of the shape. 41 | /// 42 | /// New shape with properties of existing shape 43 | public override VectorShape Duplicate() 44 | { 45 | CompoundShape duplicate = Create(); 46 | foreach (VectorShape component in components) 47 | { 48 | duplicate.AddComponent(component.Duplicate()); 49 | } 50 | 51 | return duplicate; 52 | } 53 | 54 | /// 55 | /// Add a component shape. 56 | /// 57 | public void AddComponent(VectorShape shape) 58 | { 59 | _components.Add(shape); 60 | 61 | Dirty = true; 62 | } 63 | 64 | /// 65 | /// Distance between a point and the shape. 66 | /// 67 | /// Test point 68 | /// Distance from point to nearest point on shape 69 | public override float Distance(Vector2 pt) 70 | { 71 | float distance = float.MaxValue; 72 | foreach (VectorShape component in _components) 73 | { 74 | float componentDistance = component.Distance(pt); 75 | distance = Mathf.Min(distance, componentDistance); 76 | } 77 | 78 | return distance; 79 | } 80 | 81 | /// 82 | /// Tests if a shape contains a point 83 | /// 84 | /// Test point 85 | /// Is the point inside the shape? 86 | public override bool Contains(Vector2 pt) 87 | { 88 | foreach (VectorShape component in _components) 89 | { 90 | if (component.Contains(pt)) return true; 91 | } 92 | 93 | return false; 94 | } 95 | 96 | /// 97 | /// Tests if a shape is inside a rectangle. 98 | /// 99 | /// Test rectangle 100 | /// Is the shape entirely inside the rectangle? 101 | public override bool IsInside(Rect rect) 102 | { 103 | foreach (VectorShape component in _components) 104 | { 105 | if (!component.IsInside(rect)) return false; 106 | } 107 | 108 | return true; 109 | } 110 | 111 | /// 112 | /// Rotate the shape around a point. 113 | /// 114 | /// Center of rotation 115 | /// Angle in degrees 116 | public override void RotateAround(Vector2 center, float angle) 117 | { 118 | foreach (VectorShape component in _components) 119 | { 120 | component.RotateAround(center, angle); 121 | } 122 | 123 | Dirty = true; 124 | } 125 | 126 | /// 127 | /// Change the origin of the shape. 128 | /// 129 | /// Direction to move 130 | public override void TranslateBy(Vector2 offset) 131 | { 132 | foreach (VectorShape component in _components) 133 | { 134 | component.TranslateBy(offset); 135 | } 136 | 137 | Dirty = true; 138 | } 139 | 140 | /// 141 | /// Change the size of the shape. 142 | /// 143 | /// Scaling factor to apply 144 | public override void ScaleBy(float scale) 145 | { 146 | if (scale < Mathf.Epsilon) 147 | { 148 | Debug.LogWarning("Scale must be greater than zero."); 149 | return; 150 | } 151 | 152 | foreach (VectorShape component in _components) 153 | { 154 | component.ScaleBy(scale); 155 | } 156 | 157 | Dirty = true; 158 | } 159 | 160 | /// 161 | /// Transform the shape by an arbitrary matrix. 162 | /// 163 | /// Matrix to transform shape 164 | public override void TransformBy(Matrix2D matrix) 165 | { 166 | foreach (VectorShape component in _components) 167 | { 168 | component.TransformBy(matrix); 169 | } 170 | 171 | Dirty = true; 172 | } 173 | 174 | /// 175 | /// Distance between a point and the shape. 176 | /// 177 | /// Test point 178 | /// Snap modes to consider 179 | /// Distance from point to nearest point on shape 180 | public override SnapPoint GetSnap(Vector2 pt, SnapPoint.Mode mode) 181 | { 182 | SnapPoint snap = new SnapPoint(); 183 | 184 | return snap; 185 | } 186 | 187 | /// 188 | /// Tessellate the shape into geometry data. 189 | /// 190 | protected override void GenerateGeometry() 191 | { 192 | if ((shapeGeometry != null) && (!shapeDirty)) return; 193 | 194 | shapeGeometry = new List(); 195 | 196 | foreach (VectorShape component in _components) 197 | { 198 | shapeGeometry.AddRange(component.ShapeGeometry); 199 | } 200 | 201 | shapeDirty = false; 202 | } 203 | 204 | /// 205 | /// Build a mesh for display with the VectorLineShader. 206 | /// 207 | protected override void GenerateLineMesh() 208 | { 209 | foreach (VectorShape component in _components) 210 | { 211 | shapeGeometry.AddRange(component.ShapeGeometry); 212 | } 213 | } 214 | 215 | /// 216 | /// Build a 2D bounding box for the shape. 217 | /// 218 | protected override void GenerateBounds() 219 | { 220 | if (_components.Count == 0) 221 | { 222 | shapeBounds = new Rect(); 223 | } 224 | else 225 | { 226 | shapeBounds = _components[0].ShapeBounds; 227 | for (int i = 1; i < _components.Count; i++) 228 | { 229 | shapeBounds = VectorShapeUtils.RectUnion(shapeBounds, _components[i].ShapeBounds); 230 | } 231 | } 232 | boundsDirty = false; 233 | } 234 | 235 | /// 236 | /// Build a 2D collider for the shape. 237 | /// 238 | protected override void AddColliderToGO(GameObject target) 239 | { 240 | CompositeCollider2D collider = new CompositeCollider2D(); 241 | //foreach (VectorShape component in components) 242 | //{ 243 | //} 244 | 245 | } 246 | 247 | /// 248 | /// Serialize the shape to an XML writer. 249 | /// 250 | public override void WriteToXML(XmlWriter writer, Vector2 origin, float scale) 251 | { 252 | //writer.WriteStartElement("circle"); 253 | 254 | //foreach (VectorShape component in components) 255 | //{ 256 | //} 257 | 258 | //writer.WriteEndElement(); 259 | } 260 | 261 | #if UNITY_EDITOR 262 | /// 263 | /// Draw the circle to the active camera using editor handles. 264 | /// 265 | /// Is the shape selected? 266 | /// Is it the active shape? 267 | public override void DrawEditorHandles(bool selected, bool active = false) 268 | { 269 | base.DrawEditorHandles(selected, active); 270 | 271 | foreach (VectorShape component in _components) 272 | { 273 | component.DrawEditorHandles(selected, active); 274 | } 275 | } 276 | 277 | /// 278 | /// Respond to GUI input events in editor. 279 | /// 280 | /// The current event 281 | /// Is it the active shape? 282 | /// Did the shape handle the event? 283 | public override bool HandleEditorEvent(Event currEvent, bool active) 284 | { 285 | bool handled = false; 286 | foreach (VectorShape component in _components) 287 | { 288 | handled |= component.HandleEditorEvent(currEvent, active); 289 | } 290 | 291 | return handled; 292 | } 293 | #endif 294 | } -------------------------------------------------------------------------------- /Assets/VectorShapes/CompoundShape.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7fa59c0d8f5d54a84812e847216d6e6b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 347d15c335d0e46cbbd1647d4d27ea57 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Editor/SerializedShapeDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | // IngredientDrawer 5 | [CustomPropertyDrawer(typeof(SerializedShape), true)] 6 | public class SerializedShapeDrawer : PropertyDrawer 7 | { 8 | const float padding = 4f; 9 | 10 | const float editButtonWidth = 30f; 11 | 12 | const float penLabelWidth = 24f; 13 | const float penSizeWidth = 30f; 14 | const float fillLabelWidth = 24f; 15 | 16 | const float colorChooserWidth = 24f; 17 | 18 | static GUIContent penLabelContent = new GUIContent("Pen", "Size and color of outline."); 19 | static GUIContent fillLabelContent = new GUIContent("Fill", "Color of interior."); 20 | static GUIContent editButtonContent = new GUIContent("Edit", "Modify the shape data."); 21 | 22 | // Draw the property inside the given rect 23 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 24 | { 25 | // Using BeginProperty / EndProperty on the parent property means that 26 | // prefab override logic works on the entire property. 27 | EditorGUI.BeginProperty(position, label, property); 28 | 29 | // Bail if the ScriptableObject hasn't been initialized 30 | if (property.objectReferenceValue == null) 31 | { 32 | EditorGUI.PropertyField(position, property); 33 | return; 34 | } 35 | 36 | // Draw label 37 | position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); 38 | 39 | SerializedObject propertyObject = new SerializedObject(property.objectReferenceValue); 40 | 41 | // Don't make child fields be indented 42 | var indent = EditorGUI.indentLevel; 43 | EditorGUI.indentLevel = 0; 44 | 45 | // Calculate rects 46 | Rect shapeSettingsRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); 47 | Rect colorSettingsRect = new Rect(position.x, shapeSettingsRect.yMax + padding, position.width, EditorGUIUtility.singleLineHeight); 48 | 49 | Rect editButtonRect = new Rect(shapeSettingsRect); 50 | editButtonRect.x = shapeSettingsRect.xMax - editButtonWidth; 51 | editButtonRect.width = editButtonWidth; 52 | 53 | //Rect shapeDescriptionRect = new Rect(shapeSettingsRect); 54 | //shapeDescriptionRect.x = shapeSettingsRect.x; 55 | //shapeDescriptionRect.xMax = editButtonRect.x - padding; 56 | 57 | //string shapeType = property.objectReferenceValue.GetType().Name; 58 | //int trimIndex = shapeType.LastIndexOf("Shape"); 59 | //if (trimIndex > 0) 60 | //{ 61 | // shapeType = shapeType.Substring(0, trimIndex); 62 | //} 63 | //GUIContent shapeLabelContent = new GUIContent(shapeType, "Kind of shape."); 64 | 65 | //GUIStyle rightJustified = new GUIStyle(GUI.skin.label); 66 | //rightJustified.alignment = TextAnchor.MiddleRight; 67 | //EditorGUI.LabelField(shapeDescriptionRect, shapeLabelContent, rightJustified); 68 | 69 | if (GUI.Button(editButtonRect, editButtonContent, EditorStyles.miniButton)) 70 | { 71 | SerializedShape serializedShape = property.objectReferenceValue as SerializedShape; 72 | 73 | VectorShapeEditor.OpenEditor(serializedShape); 74 | 75 | EditorUtility.SetDirty(serializedShape); 76 | } 77 | //EditorGUI.PropertyField(shapeSettingsRect, property, GUIContent.none); 78 | 79 | /* 80 | // Go right to left so we overflow back into the label space on small windows 81 | Rect fillColorRect = new Rect(colorSettingsRect); 82 | fillColorRect.width = colorChooserWidth; 83 | fillColorRect.x = colorSettingsRect.xMax - fillColorRect.width; 84 | Rect fillLabelRect = new Rect(colorSettingsRect); 85 | fillLabelRect.width = fillLabelWidth; 86 | fillLabelRect.x = fillColorRect.x - fillLabelRect.width - padding; 87 | 88 | Rect penColorRect = new Rect(colorSettingsRect); 89 | penColorRect.width = colorChooserWidth; 90 | penColorRect.x = fillLabelRect.x - penColorRect.width - padding; 91 | Rect penSizeRect = new Rect(colorSettingsRect); 92 | penSizeRect.width = penSizeWidth; 93 | penSizeRect.x = penColorRect.x - penSizeRect.width - padding; 94 | Rect penLabelRect = new Rect(colorSettingsRect); 95 | penLabelRect.width = penLabelWidth; 96 | penLabelRect.x = penSizeRect.x - penLabelRect.width; 97 | 98 | EditorGUI.BeginChangeCheck(); 99 | EditorGUI.LabelField(penLabelRect, penLabelContent); 100 | SerializedProperty penSize = propertyObject.FindProperty("penSize"); 101 | SerializedProperty colorOutline = propertyObject.FindProperty("colorOutline"); 102 | EditorGUI.PropertyField(penSizeRect, penSize, GUIContent.none); 103 | colorOutline.colorValue = EditorGUI.ColorField(penColorRect, GUIContent.none, colorOutline.colorValue, false, true, false); 104 | 105 | EditorGUI.LabelField(fillLabelRect, fillLabelContent); 106 | SerializedProperty colorFill = propertyObject.FindProperty("colorFill"); 107 | colorFill.colorValue = EditorGUI.ColorField(fillColorRect, GUIContent.none, colorFill.colorValue, false, true, false); 108 | 109 | if (EditorGUI.EndChangeCheck() || propertyObject.hasModifiedProperties) 110 | { 111 | SerializedProperty shapeDirty = propertyObject.FindProperty("shapeDirty"); 112 | if (shapeDirty != null) shapeDirty.boolValue = true; 113 | SerializedProperty meshDirty = propertyObject.FindProperty("meshDirty"); 114 | if (meshDirty != null) meshDirty.boolValue = true; 115 | 116 | propertyObject.ApplyModifiedProperties(); 117 | propertyObject.Update(); 118 | } 119 | */ 120 | 121 | // Set indent back to what it was 122 | EditorGUI.indentLevel = indent; 123 | 124 | EditorGUI.EndProperty(); 125 | } 126 | 127 | public override float GetPropertyHeight(SerializedProperty property, GUIContent label) 128 | { 129 | if (property.objectReferenceValue == null) 130 | { 131 | return EditorGUIUtility.singleLineHeight; 132 | } 133 | 134 | return EditorGUIUtility.singleLineHeight + padding; 135 | } 136 | } -------------------------------------------------------------------------------- /Assets/VectorShapes/Editor/SerializedShapeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4b2e9b1c021044776b0121c693731c7d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Editor/SingleSelectionPopup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | using UnityEditor; 4 | 5 | public class SingleSelectionPopup : PopupWindowContent 6 | { 7 | int index; 8 | GUIContent[] options; 9 | GUIStyle style; 10 | 11 | public SingleSelectionPopup(int selectedIndex, GUIContent[] displayedOptions) 12 | { 13 | index = selectedIndex; 14 | options = displayedOptions; 15 | style = GUIStyle.none; 16 | } 17 | 18 | public SingleSelectionPopup(int selectedIndex, GUIContent[] displayedOptions, GUIStyle displayedStyle) 19 | { 20 | index = selectedIndex; 21 | options = displayedOptions; 22 | style = displayedStyle; 23 | } 24 | 25 | public override Vector2 GetWindowSize() 26 | { 27 | Vector2 size = Vector2.one; 28 | 29 | for (int i = 0; i < options.Length; i++) 30 | { 31 | Vector2 contentSize = style.CalcSize(options[i]); 32 | size.x = Mathf.Max(size.x, contentSize.x); 33 | size.y += contentSize.y; 34 | } 35 | 36 | return size; 37 | } 38 | 39 | public override void OnGUI(Rect rect) 40 | { 41 | Rect labelRect = rect; 42 | 43 | for (int i = 0; i < options.Length; i++) 44 | { 45 | Vector2 contentSize = style.CalcSize(options[i]); 46 | labelRect.width = Mathf.Min(rect.width, contentSize.x); 47 | labelRect.height = contentSize.y; 48 | 49 | GUI.Label(labelRect, options[i], style); 50 | 51 | labelRect.y += labelRect.height; 52 | } 53 | } 54 | 55 | public override void OnOpen() 56 | { 57 | Debug.Log("Popup opened: " + this); 58 | } 59 | 60 | public override void OnClose() 61 | { 62 | Debug.Log("Popup closed: " + this); 63 | } 64 | } -------------------------------------------------------------------------------- /Assets/VectorShapes/Editor/SingleSelectionPopup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 797519715515d413c967199d201e8455 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Editor/VectorShapeEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d91219672378e4e7c92c87f239c75f5d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/EllipseShape.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml; 3 | using UnityEngine; 4 | #if UNITY_EDITOR 5 | using UnityEditor; 6 | #endif 7 | using Unity.VectorGraphics; 8 | 9 | /// 10 | /// Ellipse equations are taken from Luc Maisonobe's 11 | /// "Quick computation of the distance between a point and an ellipse" 12 | /// 13 | 14 | [System.Serializable] 15 | /// 16 | /// Vector ellipse. 17 | /// 18 | public class EllipseShape : VectorShape 19 | { 20 | /// 21 | /// Position of center. 22 | /// 23 | [SerializeField] 24 | protected Vector2 position; 25 | 26 | /// 27 | /// Major axis of the ellipse. 28 | /// 29 | [SerializeField] 30 | protected Vector2 majorAxis; 31 | 32 | /// 33 | /// Eccentricty of ellipse (ratio of focuss distance to major axis). 34 | /// 35 | [SerializeField] 36 | protected float eccentricity; 37 | 38 | /// 39 | /// Starting angle for elliptical arcs (in radians). 40 | /// 41 | [SerializeField] 42 | protected float startAngle = 0f; 43 | 44 | /// 45 | /// Sweep angle for elliptical arcs (in radians). 46 | /// 47 | [SerializeField] 48 | protected float sweepAngle = Mathf.PI * 2f; 49 | 50 | /// 51 | /// Position of center. 52 | /// 53 | public Vector2 Position 54 | { 55 | set 56 | { 57 | position = value; 58 | Dirty = true; 59 | } 60 | get 61 | { 62 | return position; 63 | } 64 | } 65 | 66 | /// 67 | /// Eccentricty of ellipse (ratio of focuss distance to major axis). 68 | /// 69 | public float Eccentricity 70 | { 71 | set 72 | { 73 | if ((eccentricity >= 0f) && (eccentricity < 1f)) 74 | { 75 | eccentricity = value; 76 | Dirty = true; 77 | } 78 | else 79 | { 80 | Debug.LogWarning("Invalid value for eccentricity: " + value); 81 | } 82 | } 83 | get 84 | { 85 | return eccentricity; 86 | } 87 | } 88 | 89 | /// 90 | /// Major axis of the ellipse. 91 | /// 92 | public Vector2 MajorAxis 93 | { 94 | set 95 | { 96 | majorAxis = value; 97 | Dirty = true; 98 | } 99 | get 100 | { 101 | return majorAxis; 102 | } 103 | } 104 | 105 | /// 106 | /// Minor axis of the ellipse (read only). 107 | /// 108 | public Vector2 MinorAxis 109 | { 110 | get 111 | { 112 | Vector2 minorAxis = Vector2.Perpendicular(majorAxis) * Mathf.Sqrt(1f - (eccentricity * eccentricity)); 113 | return minorAxis; 114 | } 115 | } 116 | 117 | /// 118 | /// Starting angle for elliptical arcs (in degrees). 119 | /// 120 | public float StartAngle 121 | { 122 | set 123 | { 124 | startAngle = value * Mathf.Deg2Rad; 125 | Dirty = true; 126 | } 127 | get 128 | { 129 | return startAngle * Mathf.Rad2Deg; 130 | } 131 | } 132 | 133 | /// 134 | /// Sweep angle for elliptical arcs (in degrees). 135 | /// 136 | public float SweepAngle 137 | { 138 | set 139 | { 140 | sweepAngle = value * Mathf.Deg2Rad; 141 | if (Mathf.Approximately(value, 0f) || (Mathf.Abs(value) >= 360f)) 142 | { 143 | closed = true; 144 | } 145 | else 146 | { 147 | closed = false; 148 | } 149 | Dirty = true; 150 | } 151 | get 152 | { 153 | return sweepAngle * Mathf.Rad2Deg; 154 | } 155 | } 156 | 157 | protected EllipseShape() 158 | { 159 | } 160 | 161 | /// 162 | /// New ellipse from center point and axis radii (SVG format). 163 | /// 164 | /// Center of circle 165 | /// Radius of ellipse on X axis 166 | /// Radius of ellipse on Y axis 167 | /// Rotation from x-axis (in degrees) 168 | public EllipseShape(Vector2 center, float radX, float radY, float rotation = 0f) 169 | { 170 | position = center; 171 | if (radX >= radY) 172 | { 173 | majorAxis = Vector2.right * radX; 174 | eccentricity = Mathf.Sin(Mathf.Atan2(radX, radY)); 175 | } 176 | else 177 | { 178 | majorAxis = Vector2.up * radY; 179 | eccentricity = Mathf.Sin(Mathf.Atan2(radY, radX)); 180 | } 181 | 182 | majorAxis = Matrix2D.RotateRH(rotation * Mathf.Deg2Rad).MultiplyVector(majorAxis); 183 | } 184 | 185 | /// 186 | /// New ellipse from center point, major axis, and minor axis ratio (DXF format). 187 | /// 188 | /// Center of circle 189 | /// Major axis of the ellipse 190 | /// Ratio of minor axis to major axis 191 | public EllipseShape(Vector2 center, Vector2 major, float ratio) 192 | { 193 | position = center; 194 | majorAxis = major; 195 | float majorLength = major.magnitude; 196 | float minorLength = major.magnitude * ratio; 197 | 198 | eccentricity = Mathf.Sqrt((majorLength * majorLength) - (minorLength * minorLength)) / majorLength; 199 | } 200 | 201 | /// 202 | /// New elliptical arc from center point, major axis, minor axis ration, and angles. 203 | /// 204 | /// Center of circle 205 | /// Major axis of the ellipse 206 | /// Ratio of minor axis to major axis 207 | /// Starting angle of arc (in degrees) 208 | /// Sweep of arc (in degrees) 209 | public EllipseShape(Vector2 center, Vector2 major, float ratio, float angle, float sweep) 210 | { 211 | position = center; 212 | majorAxis = major; 213 | float majorLength = major.magnitude; 214 | float minorLength = major.magnitude * ratio; 215 | 216 | eccentricity = Mathf.Sqrt((majorLength * majorLength) - (minorLength * minorLength)) / majorLength; 217 | 218 | startAngle = angle * Mathf.Deg2Rad; 219 | sweepAngle = sweep * Mathf.Deg2Rad; 220 | 221 | if (Mathf.Approximately(sweep, 0f) || (Mathf.Abs(sweep) >= 360f)) 222 | { 223 | closed = true; 224 | } 225 | else 226 | { 227 | closed = false; 228 | } 229 | } 230 | 231 | protected static EllipseShape Create() 232 | { 233 | //EllipseShape shape = ScriptableObject.CreateInstance(); 234 | EllipseShape shape = new EllipseShape(); 235 | 236 | return shape; 237 | } 238 | 239 | /// 240 | /// New ellipse from center point and axis radii (SVG format). 241 | /// 242 | /// Center of circle 243 | /// Radius of ellipse on X axis 244 | /// Radius of ellipse on Y axis 245 | /// Rotation from x-axis (in degrees) 246 | public static EllipseShape Create(Vector2 center, float radX, float radY, float rotation = 0f) 247 | { 248 | EllipseShape shape = Create(); 249 | 250 | shape.position = center; 251 | if (radX >= radY) 252 | { 253 | shape.majorAxis = Vector2.right * radX; 254 | shape.eccentricity = Mathf.Sin(Mathf.Atan2(radX, radY)); 255 | } 256 | else 257 | { 258 | shape.majorAxis = Vector2.up * radY; 259 | shape.eccentricity = Mathf.Sin(Mathf.Atan2(radY, radX)); 260 | } 261 | 262 | shape.majorAxis = Matrix2D.RotateRH(rotation * Mathf.Deg2Rad).MultiplyVector(shape.majorAxis); 263 | 264 | return shape; 265 | } 266 | 267 | /// 268 | /// New ellipse from center point, major axis, and minor axis ratio (DXF format). 269 | /// 270 | /// Center of circle 271 | /// Major axis of the ellipse 272 | /// Ratio of minor axis to major axis 273 | public static EllipseShape Create(Vector2 center, Vector2 major, float ratio) 274 | { 275 | EllipseShape shape = Create(); 276 | 277 | shape.position = center; 278 | shape.majorAxis = major; 279 | float majorLength = major.magnitude; 280 | float minorLength = major.magnitude * ratio; 281 | 282 | shape.eccentricity = Mathf.Sqrt((majorLength * majorLength) - (minorLength * minorLength)) / majorLength; 283 | 284 | return shape; 285 | } 286 | 287 | /// 288 | /// New elliptical arc from center point, major axis, minor axis ration, and angles. 289 | /// 290 | /// Center of circle 291 | /// Major axis of the ellipse 292 | /// Ratio of minor axis to major axis 293 | /// Starting angle of arc (in degrees) 294 | /// Sweep of arc (in degrees) 295 | public static EllipseShape Create(Vector2 center, Vector2 major, float ratio, float angle, float sweep) 296 | { 297 | EllipseShape shape = Create(); 298 | 299 | shape.position = center; 300 | shape.majorAxis = major; 301 | float majorLength = major.magnitude; 302 | float minorLength = major.magnitude * ratio; 303 | 304 | shape.eccentricity = Mathf.Sqrt((majorLength * majorLength) - (minorLength * minorLength)) / majorLength; 305 | 306 | shape.startAngle = angle * Mathf.Deg2Rad; 307 | shape.sweepAngle = sweep * Mathf.Deg2Rad; 308 | 309 | if (Mathf.Approximately(sweep, 0f) || (Mathf.Abs(sweep) >= 360f)) 310 | { 311 | shape.closed = true; 312 | } 313 | else 314 | { 315 | shape.closed = false; 316 | } 317 | 318 | return shape; 319 | } 320 | 321 | /// 322 | /// Copy of the shape. 323 | /// 324 | /// New shape with properties of existing shape 325 | public override VectorShape Duplicate() 326 | { 327 | float ratio = MinorAxis.magnitude / MajorAxis.magnitude; 328 | return Create(position, majorAxis, ratio, startAngle * Mathf.Rad2Deg, sweepAngle * Mathf.Rad2Deg); 329 | } 330 | 331 | private static Vector2 ClosestEllipsePoint(Vector2 point, float semiMajor, float semiMinor) 332 | { 333 | Vector2 p = new Vector2(Mathf.Abs(point.x), Mathf.Abs(point.y)); 334 | 335 | float t = Mathf.PI / 4; 336 | 337 | float a = semiMajor; 338 | float b = semiMinor; 339 | 340 | Vector2 pt; 341 | 342 | float cosT, sinT, deltaC, deltaT; 343 | Vector2 e; Vector2 r; Vector2 q; 344 | 345 | for (int i = 0; i < 3; i++) 346 | { 347 | cosT = Mathf.Cos(t); 348 | sinT = Mathf.Sin(t); 349 | 350 | pt.x = a * cosT; 351 | pt.y = b * sinT; 352 | 353 | e.x = (a * a - b * b) * (cosT * cosT * cosT) / a; 354 | e.y = (b * b - a * a) * (sinT * sinT * sinT) / b; 355 | 356 | r = pt - e; 357 | q = p - e; 358 | 359 | deltaC = r.magnitude * Mathf.Asin((r.x * q.y - r.y * q.x) / (r.magnitude * q.magnitude)); 360 | deltaT = deltaC / Mathf.Sqrt(a * a + b * b - pt.sqrMagnitude); 361 | 362 | t += deltaT; 363 | t = Mathf.Clamp(t, 0f, Mathf.PI / 2); 364 | } 365 | 366 | pt.x = a * Mathf.Cos(t) * Mathf.Sign(point.x); 367 | pt.y = b * Mathf.Sin(t) * Mathf.Sign(point.y); 368 | 369 | return pt; 370 | } 371 | 372 | /// 373 | /// Closest point on the shape to a point. 374 | /// 375 | /// Test point 376 | /// Closest point on shape 377 | public Vector2 ClosestPoint(Vector2 pt) 378 | { 379 | float theta = Mathf.Atan2(MajorAxis.y, MajorAxis.x); 380 | 381 | Vector2 major = MajorAxis; 382 | Vector2 minor = MinorAxis; 383 | 384 | Matrix2D matrix = Matrix2D.RotateRH(-theta) * Matrix2D.Translate(-position); 385 | 386 | Vector2 standardPt = matrix.MultiplyPoint(pt); 387 | 388 | Vector2 closestPt = ClosestEllipsePoint(standardPt, major.magnitude, minor.magnitude); 389 | 390 | closestPt = matrix.Inverse().MultiplyPoint(closestPt); 391 | 392 | return closestPt; 393 | } 394 | 395 | /// 396 | /// Distance between a point and the shape. 397 | /// 398 | /// Test point 399 | /// Distance from point to nearest point on shape 400 | public override float Distance(Vector2 pt) 401 | { 402 | Vector2 closest = ClosestPoint(pt); 403 | 404 | return Vector2.Distance(pt, closest); 405 | } 406 | 407 | /// 408 | /// Tests if a shape contains a point 409 | /// 410 | /// Test point 411 | /// Is the point inside the shape? 412 | public override bool Contains(Vector2 pt) 413 | { 414 | Vector2 focus1 = position + majorAxis * eccentricity; 415 | Vector2 focus2 = position - majorAxis * eccentricity; 416 | 417 | float distance1 = Vector2.Distance(pt, focus1); 418 | float distance2 = Vector2.Distance(pt, focus2); 419 | return ((distance1 + distance2) < (majorAxis.magnitude * 2f)); 420 | } 421 | 422 | /// 423 | /// Tests if a shape is inside a rectangle. 424 | /// 425 | /// Test rectangle 426 | /// Is the shape entirely inside the rectangle? 427 | public override bool IsInside(Rect rect) 428 | { 429 | if (boundsDirty) GenerateBounds(); 430 | 431 | if (rect.xMin > shapeBounds.xMin) return false; 432 | if (rect.xMax < shapeBounds.xMax) return false; 433 | if (rect.yMin > shapeBounds.yMin) return false; 434 | if (rect.yMax < shapeBounds.yMax) return false; 435 | 436 | return true; 437 | } 438 | 439 | /// 440 | /// Rotate the shape around a point. 441 | /// 442 | /// Center of rotation 443 | /// Angle in degrees 444 | public override void RotateAround(Vector2 center, float angle) 445 | { 446 | Matrix2D matrix = Matrix2D.Translate(center) * Matrix2D.RotateRH(angle * Mathf.Deg2Rad) * Matrix2D.Translate(-center); 447 | position = matrix.MultiplyPoint(position); 448 | majorAxis = matrix.MultiplyVector(majorAxis); 449 | 450 | Dirty = true; 451 | } 452 | 453 | /// 454 | /// Change the origin of the shape. 455 | /// 456 | /// Direction to move 457 | public override void TranslateBy(Vector2 offset) 458 | { 459 | position += offset; 460 | 461 | Dirty = true; 462 | } 463 | 464 | /// 465 | /// Transform the shape by an arbitrary matrix. 466 | /// 467 | /// Matrix to transform shape 468 | public override void TransformBy(Matrix2D matrix) 469 | { 470 | position = matrix.MultiplyPoint(position); 471 | majorAxis = matrix.MultiplyVector(majorAxis); 472 | 473 | Dirty = true; 474 | } 475 | 476 | /// 477 | /// Change the size of the shape. 478 | /// 479 | /// Scaling factor to apply 480 | public override void ScaleBy(float scale) 481 | { 482 | if (scale < Mathf.Epsilon) 483 | { 484 | Debug.LogWarning("Scale must be greater than zero."); 485 | return; 486 | } 487 | 488 | position *= scale; 489 | majorAxis *= scale; 490 | 491 | Dirty = true; 492 | } 493 | 494 | /// 495 | /// Distance between a point and the shape. 496 | /// 497 | /// Test point 498 | /// Snap modes to consider 499 | /// Distance from point to nearest point on shape 500 | public override SnapPoint GetSnap(Vector2 pt, SnapPoint.Mode mode) 501 | { 502 | SnapPoint snap = new SnapPoint(); 503 | float distance = float.MaxValue; 504 | 505 | if ((mode & SnapPoint.Mode.Center) != 0) 506 | { 507 | Vector2[] centerPoints = 508 | { 509 | position, 510 | position + majorAxis * eccentricity, 511 | position - majorAxis * eccentricity, 512 | }; 513 | 514 | foreach (Vector2 testPt in centerPoints) 515 | { 516 | float d = Vector2.Distance(pt, testPt); 517 | if (d < distance) 518 | { 519 | distance = d; 520 | snap.mode = SnapPoint.Mode.Midpoint; 521 | snap.point = testPt; 522 | } 523 | } 524 | } 525 | 526 | if ((mode & SnapPoint.Mode.Midpoint) != 0) 527 | { 528 | Vector2[] midPoints = 529 | { 530 | position + majorAxis, 531 | position + MinorAxis, 532 | position - majorAxis, 533 | position - MinorAxis, 534 | }; 535 | 536 | foreach (Vector2 testPt in midPoints) 537 | { 538 | float d = Vector2.Distance(pt, testPt); 539 | if (d < distance) 540 | { 541 | distance = d; 542 | snap.mode = SnapPoint.Mode.Midpoint; 543 | snap.point = testPt; 544 | } 545 | } 546 | } 547 | 548 | if ((mode & SnapPoint.Mode.Edge) != 0) 549 | { 550 | Vector2 closest = ClosestPoint(pt); 551 | float d = Vector2.Distance(pt, closest); 552 | 553 | if (d < distance) 554 | { 555 | distance = d; 556 | snap.mode = SnapPoint.Mode.Edge; 557 | snap.point = closest; 558 | } 559 | } 560 | 561 | return snap; 562 | } 563 | 564 | protected BezierSegment[] GenerateSegments() 565 | { 566 | int numCurves = 4; // Supposed to calculate from max error 567 | float theta = Mathf.Atan2(MajorAxis.y, MajorAxis.x); 568 | 569 | BezierSegment[] segments = new BezierSegment[numCurves]; 570 | float deltaAngle = sweepAngle / numCurves; 571 | float sinTheta = Mathf.Sin(theta); 572 | float cosTheta = Mathf.Cos(theta); 573 | float angleB = startAngle; 574 | float a = MajorAxis.magnitude; 575 | float b = MinorAxis.magnitude; 576 | float t = Mathf.Tan(0.5f * deltaAngle); 577 | float alpha = Mathf.Sin(deltaAngle) * (Mathf.Sqrt(4f + 3f * t * t) - 1f) / 3f; 578 | 579 | float sinAngleB = Mathf.Sin(angleB); 580 | float cosAngleB = Mathf.Cos(angleB); 581 | float aSinAngleB = a * sinAngleB; 582 | float aCosAngleB = a * cosAngleB; 583 | float bSinAngleB = b * sinAngleB; 584 | float bCosAngleB = b * cosAngleB; 585 | Vector2 ptB = new Vector2(); 586 | ptB.x = position.x + aCosAngleB * cosTheta - bSinAngleB * sinTheta; 587 | ptB.y = position.y + aCosAngleB * sinTheta + bSinAngleB * cosTheta; 588 | Vector2 dotB = new Vector2(); 589 | dotB.x = -aSinAngleB * cosTheta - bCosAngleB * sinTheta; 590 | dotB.y = -aSinAngleB * sinTheta + bCosAngleB * cosTheta; 591 | 592 | for (int i = 0; i < numCurves; ++i) 593 | { 594 | float angleA = angleB; 595 | Vector2 ptA = ptB; 596 | Vector2 dotA = dotB; 597 | 598 | angleB += deltaAngle; 599 | sinAngleB = Mathf.Sin(angleB); 600 | cosAngleB = Mathf.Cos(angleB); 601 | aSinAngleB = a * sinAngleB; 602 | aCosAngleB = a * cosAngleB; 603 | bSinAngleB = b * sinAngleB; 604 | bCosAngleB = b * cosAngleB; 605 | ptB.x = position.x + aCosAngleB * cosTheta - bSinAngleB * sinTheta; 606 | ptB.y = position.y + aCosAngleB * sinTheta + bSinAngleB * cosTheta; 607 | dotB.x = -aSinAngleB * cosTheta - bCosAngleB * sinTheta; 608 | dotB.y = -aSinAngleB * sinTheta + bCosAngleB * cosTheta; 609 | 610 | segments[i].P0 = ptA; 611 | segments[i].P1.x = ptA.x + alpha * dotA.x; 612 | segments[i].P1.y = ptA.y + alpha * dotA.y; 613 | segments[i].P2.x = ptB.x - alpha * dotB.x; 614 | segments[i].P2.y = ptB.y - alpha * dotB.y; 615 | segments[i].P3 = ptB; 616 | } 617 | 618 | return segments; 619 | } 620 | 621 | /// 622 | /// Tessellate the shape into geometry data. 623 | /// 624 | protected override void GenerateGeometry() 625 | { 626 | if ((shapeGeometry != null) && (!shapeDirty)) return; 627 | 628 | Shape ellipse = new Shape(); 629 | 630 | ellipse.PathProps = new PathProperties() 631 | { 632 | Stroke = new Stroke() 633 | { 634 | Color = colorOutline, 635 | HalfThickness = penSize / 2f * penToMeshScale 636 | } 637 | }; 638 | if (colorFill != Color.clear) 639 | { 640 | ellipse.Fill = new SolidFill() 641 | { 642 | Color = colorFill 643 | }; 644 | } 645 | 646 | BezierSegment[] segments = GenerateSegments(); 647 | 648 | ellipse.Contours = new BezierContour[1]; 649 | ellipse.Contours[0] = new BezierContour(); 650 | ellipse.Contours[0].Segments = VectorUtils.BezierSegmentsToPath(segments); 651 | 652 | shapeNode = new SceneNode() 653 | { 654 | Transform = matrixTransform, 655 | Shapes = new List 656 | { 657 | ellipse 658 | } 659 | }; 660 | 661 | tessellationScene.Root = shapeNode; 662 | shapeGeometry = VectorUtils.TessellateScene(tessellationScene, tessellationOptions); 663 | 664 | shapeDirty = false; 665 | } 666 | 667 | /// 668 | /// Build a mesh for display with the VectorLineShader. 669 | /// 670 | protected override void GenerateLineMesh() 671 | { 672 | BezierSegment[] segments = GenerateSegments(); 673 | 674 | lineBuilder.BeginPolyLine(segments[0].P0); 675 | 676 | for (int i = 0; i < segments.Length; i++) 677 | { 678 | BezierSegment segment = segments[i]; 679 | lineBuilder.CurveTo(segment.P1, segment.P2, segment.P3, 8); 680 | } 681 | 682 | lineBuilder.EndPolyLine(closed); 683 | } 684 | 685 | /// 686 | /// Build a 2D bounding box for the shape. 687 | /// 688 | protected override void GenerateBounds() 689 | { 690 | float a = MajorAxis.magnitude; 691 | float b = MinorAxis.magnitude; 692 | float theta = Mathf.Atan2(MajorAxis.y, MajorAxis.x); 693 | 694 | float sinTheta = Mathf.Sin(theta); 695 | float cosTheta = Mathf.Cos(theta); 696 | float extentX = Mathf.Sqrt((a * a * cosTheta * cosTheta) + (b * b * sinTheta * sinTheta)); 697 | float extentY = Mathf.Sqrt((a * a * sinTheta * sinTheta) + (b * b * cosTheta * cosTheta)); 698 | 699 | shapeBounds.xMin = position.x - extentX; 700 | shapeBounds.xMax = position.x + extentX; 701 | shapeBounds.yMin = position.y - extentY; 702 | shapeBounds.yMax = position.y + extentY; 703 | 704 | boundsDirty = false; 705 | } 706 | 707 | /// 708 | /// Build a 2D collider for the shape. 709 | /// 710 | protected override void AddColliderToGO(GameObject target) 711 | { 712 | CircleCollider2D[] colliders = target.GetComponents(); 713 | CircleCollider2D collider = null; 714 | 715 | for (int i = 0; i < colliders.Length; i++) 716 | { 717 | if (colliders[i].name == this.guid) 718 | { 719 | collider = colliders[i]; 720 | } 721 | } 722 | 723 | // HACK - this is REALLY wrong 724 | if (collider == null) 725 | { 726 | collider = collider.gameObject.AddComponent(); 727 | collider.name = this.guid; 728 | } 729 | 730 | collider.offset = position; 731 | collider.radius = majorAxis.magnitude; 732 | } 733 | 734 | /// 735 | /// Serialize the shape to an XML writer. 736 | /// 737 | public override void WriteToXML(XmlWriter writer, Vector2 origin, float scale) 738 | { 739 | } 740 | 741 | #if UNITY_EDITOR 742 | /// 743 | /// Draw the circle to the active camera using editor handles. 744 | /// 745 | /// Is the shape selected? 746 | /// Is it the active shape? 747 | public override void DrawEditorHandles(bool selected, bool active = false) 748 | { 749 | base.DrawEditorHandles(selected, active); 750 | 751 | /* 752 | Vector3 handleP0 = new Vector3(); 753 | Vector3 handleP1 = new Vector3(); 754 | Vector3 handleP2 = new Vector3(); 755 | Vector3 handleP3 = new Vector3(); 756 | 757 | if (colorFill != Color.clear) 758 | { 759 | Color colorPrev = Handles.color; 760 | Vector3 center3 = new Vector3(position.x, position.y, 0); 761 | 762 | Handles.color = colorFill; 763 | Handles.DrawSolidDisc(center3, Vector3.forward, radius); 764 | Handles.color = colorPrev; 765 | } 766 | 767 | // Draw the circle using 4 Bezier curves so we can control the pen size 768 | handleP0.x = position.x; 769 | handleP0.y = position.y + radius; 770 | handleP1.x = position.x + radius * bezierCircleConst; 771 | handleP1.y = position.y + radius; 772 | handleP2.x = position.x + radius; 773 | handleP2.y = position.y + radius * bezierCircleConst; 774 | handleP3.x = position.x + radius; 775 | handleP3.y = position.y; 776 | Handles.DrawBezier(handleP0, handleP3, handleP1, handleP2, colorOutline, handleDrawTexture, penSize); 777 | 778 | handleP0.x = position.x + radius; 779 | handleP0.y = position.y; 780 | handleP1.x = position.x + radius; 781 | handleP1.y = position.y - radius * bezierCircleConst; 782 | handleP2.x = position.x + radius * bezierCircleConst; 783 | handleP2.y = position.y - radius; 784 | handleP3.x = position.x; 785 | handleP3.y = position.y - radius; 786 | Handles.DrawBezier(handleP0, handleP3, handleP1, handleP2, colorOutline, handleDrawTexture, penSize); 787 | 788 | handleP0.x = position.x; 789 | handleP0.y = position.y - radius; 790 | handleP1.x = position.x - radius * bezierCircleConst; 791 | handleP1.y = position.y - radius; 792 | handleP2.x = position.x - radius; 793 | handleP2.y = position.y - radius * bezierCircleConst; 794 | handleP3.x = position.x - radius; 795 | handleP3.y = position.y; 796 | Handles.DrawBezier(handleP0, handleP3, handleP1, handleP2, colorOutline, handleDrawTexture, penSize); 797 | 798 | handleP0.x = position.x - radius; 799 | handleP0.y = position.y; 800 | handleP1.x = position.x - radius; 801 | handleP1.y = position.y + radius * bezierCircleConst; 802 | handleP2.x = position.x - radius * bezierCircleConst; 803 | handleP2.y = position.y + radius; 804 | handleP3.x = position.x; 805 | handleP3.y = position.y + radius; 806 | Handles.DrawBezier(handleP0, handleP3, handleP1, handleP2, colorOutline, handleDrawTexture, penSize); 807 | */ 808 | 809 | if (active) 810 | { 811 | 812 | } 813 | } 814 | 815 | /// 816 | /// Respond to GUI input events in editor. 817 | /// 818 | /// The current event 819 | /// Is it the active shape? 820 | /// Did the shape handle the event? 821 | public override bool HandleEditorEvent(Event currEvent, bool active) 822 | { 823 | return false; 824 | } 825 | #endif 826 | 827 | } -------------------------------------------------------------------------------- /Assets/VectorShapes/EllipseShape.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 37d662c8881e7473ea893cc9c063c031 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/PointShape.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml; 3 | using UnityEngine; 4 | #if UNITY_EDITOR 5 | using UnityEditor; 6 | #endif 7 | using Unity.VectorGraphics; 8 | 9 | [System.Serializable] 10 | /// 11 | /// Vector point. 12 | /// 13 | public class PointShape : VectorShape 14 | { 15 | /// 16 | /// Visual size of point. 17 | /// 18 | public float pointRadius = 0.1f; 19 | 20 | /// 21 | /// Position of point. 22 | /// 23 | public Vector2 position; 24 | 25 | protected PointShape() 26 | { 27 | } 28 | 29 | /// 30 | /// New point from location. 31 | /// 32 | /// Position of point 33 | public PointShape(Vector2 location) 34 | { 35 | position = location; 36 | } 37 | 38 | /// 39 | /// New point from coordinates. 40 | /// 41 | /// X position of point 42 | /// X position of point 43 | public PointShape(float x, float y) 44 | { 45 | position = new Vector2(x, y); 46 | } 47 | 48 | protected static PointShape Create() 49 | { 50 | //PointShape shape = ScriptableObject.CreateInstance(); 51 | PointShape shape = new PointShape(); 52 | 53 | return shape; 54 | } 55 | 56 | /// 57 | /// New point from location. 58 | /// 59 | /// Position of point 60 | public static PointShape Create(Vector2 location) 61 | { 62 | PointShape shape = Create(); 63 | 64 | shape.position = location; 65 | 66 | return shape; 67 | } 68 | 69 | /// 70 | /// New point from coordinates. 71 | /// 72 | /// X position of point 73 | /// X position of point 74 | public static PointShape Create(float x, float y) 75 | { 76 | PointShape shape = Create(); 77 | 78 | shape.position = new Vector2(x, y); 79 | 80 | return shape; 81 | } 82 | 83 | /// 84 | /// Copy of the shape. 85 | /// 86 | /// New shape with properties of existing shape 87 | public override VectorShape Duplicate() 88 | { 89 | return Create(position); 90 | } 91 | 92 | /// 93 | /// Distance between a point and the shape. 94 | /// 95 | /// Test point 96 | /// Distance from point to nearest point on shape 97 | public override float Distance(Vector2 pt) 98 | { 99 | return Vector2.Distance(pt, position); 100 | } 101 | 102 | /// 103 | /// Tests if a shape contains a point 104 | /// 105 | /// Test point 106 | /// Is the point inside the shape? 107 | public override bool Contains(Vector2 pt) 108 | { 109 | return false; 110 | } 111 | 112 | /// 113 | /// Tests if a shape is inside a rectangle. 114 | /// 115 | /// Test rectangle 116 | /// Is the shape entirely inside the rectangle? 117 | public override bool IsInside(Rect rect) 118 | { 119 | return rect.Contains(position); 120 | } 121 | 122 | /// 123 | /// Rotate the shape around a point. 124 | /// 125 | /// Center of rotation 126 | /// Angle in degrees 127 | public override void RotateAround(Vector2 center, float angle) 128 | { 129 | Matrix2D matrix = Matrix2D.Translate(center) * Matrix2D.RotateRH(angle * Mathf.Deg2Rad) * Matrix2D.Translate(-center); 130 | position = matrix.MultiplyPoint(position); 131 | 132 | Dirty = true; 133 | } 134 | 135 | /// 136 | /// Change the origin of the shape. 137 | /// 138 | /// Direction to move 139 | public override void TranslateBy(Vector2 offset) 140 | { 141 | position += offset; 142 | 143 | Dirty = true; 144 | } 145 | 146 | /// 147 | /// Change the size of the shape. 148 | /// 149 | /// Scaling factor to apply 150 | public override void ScaleBy(float scale) 151 | { 152 | if (scale < Mathf.Epsilon) 153 | { 154 | Debug.LogWarning("Scale must be greater than zero."); 155 | return; 156 | } 157 | 158 | position *= scale; 159 | 160 | Dirty = true; 161 | } 162 | 163 | /// 164 | /// Transform the shape by an arbitrary matrix. 165 | /// 166 | /// Matrix to transform shape 167 | public override void TransformBy(Matrix2D matrix) 168 | { 169 | position = matrix.MultiplyPoint(position); 170 | 171 | Dirty = true; 172 | } 173 | 174 | /// 175 | /// Distance between a point and the shape. 176 | /// 177 | /// Test point 178 | /// Snap modes to consider 179 | /// Distance from point to nearest point on shape 180 | public override SnapPoint GetSnap(Vector2 pt, SnapPoint.Mode mode) 181 | { 182 | SnapPoint snap = new SnapPoint(); 183 | 184 | snap.point = position; 185 | 186 | if ((mode & SnapPoint.Mode.Center) != 0) snap.mode = SnapPoint.Mode.Center; 187 | else if ((mode & SnapPoint.Mode.Endpoint) != 0) snap.mode = SnapPoint.Mode.Endpoint; 188 | else if ((mode & SnapPoint.Mode.Midpoint) != 0) snap.mode = SnapPoint.Mode.Midpoint; 189 | else if ((mode & SnapPoint.Mode.Edge) != 0) snap.mode = SnapPoint.Mode.Edge; 190 | 191 | return snap; 192 | } 193 | 194 | /// 195 | /// Tessellate the shape into geometry data. 196 | /// 197 | protected override void GenerateGeometry() 198 | { 199 | if ((shapeGeometry != null) && (!shapeDirty)) return; 200 | 201 | var seg1 = VectorUtils.MakePathLine( 202 | new Vector2(position.x, position.y + pointRadius), 203 | new Vector2(position.x, position.y - pointRadius) 204 | ); 205 | var seg2 = VectorUtils.MakePathLine( 206 | new Vector2(position.x + pointRadius, position.y), 207 | new Vector2(position.x - pointRadius, position.y) 208 | ); 209 | 210 | PathProperties pathProps = new PathProperties() 211 | { 212 | Stroke = new Stroke() 213 | { 214 | Color = colorOutline, 215 | HalfThickness = penSize / 2f * penToMeshScale 216 | } 217 | }; 218 | 219 | Shape segment1 = new Shape() 220 | { 221 | Contours = new BezierContour[] 222 | { 223 | new BezierContour {Segments = seg1} 224 | }, 225 | PathProps = pathProps 226 | }; 227 | 228 | Shape segment2 = new Shape() 229 | { 230 | Contours = new BezierContour[] 231 | { 232 | new BezierContour {Segments = seg2} 233 | }, 234 | PathProps = pathProps 235 | }; 236 | 237 | shapeNode = new SceneNode() 238 | { 239 | Transform = matrixTransform, 240 | Shapes = new List 241 | { 242 | segment1, segment2 243 | } 244 | }; 245 | 246 | tessellationScene.Root = shapeNode; 247 | 248 | shapeGeometry = VectorUtils.TessellateScene(tessellationScene, tessellationOptions); 249 | shapeDirty = false; 250 | } 251 | 252 | /// 253 | /// Build a mesh for display with the VectorLineShader. 254 | /// 255 | protected override void GenerateLineMesh() 256 | { 257 | lineBuilder.BeginPolyLine(new Vector2(position.x, position.y + pointRadius)); 258 | lineBuilder.LineTo(new Vector2(position.x, position.y - pointRadius)); 259 | lineBuilder.EndPolyLine(); 260 | 261 | lineBuilder.BeginPolyLine(new Vector2(position.x + pointRadius, position.y)); 262 | lineBuilder.LineTo(new Vector2(position.x - pointRadius, position.y)); 263 | lineBuilder.EndPolyLine(); 264 | } 265 | 266 | /// 267 | /// Build a 2D bounding box for the shape. 268 | /// 269 | protected override void GenerateBounds() 270 | { 271 | shapeBounds = new Rect(position, Vector2.zero); 272 | boundsDirty = false; 273 | } 274 | 275 | /// 276 | /// Build a 2D collider for the shape. 277 | /// 278 | protected override void AddColliderToGO(GameObject target) 279 | { 280 | CircleCollider2D[] colliders = target.GetComponents(); 281 | CircleCollider2D collider = null; 282 | 283 | for (int i = 0; i < colliders.Length; i++) 284 | { 285 | if (colliders[i].name == this.guid) 286 | { 287 | collider = colliders[i]; 288 | } 289 | } 290 | 291 | if (collider == null) 292 | { 293 | collider = collider.gameObject.AddComponent(); 294 | collider.name = this.guid; 295 | } 296 | 297 | collider.offset = position; 298 | collider.radius = pointRadius; 299 | } 300 | 301 | /// 302 | /// Serialize the shape to an XML writer. 303 | /// 304 | public override void WriteToXML(XmlWriter writer, Vector2 origin, float scale) 305 | { 306 | } 307 | 308 | #if UNITY_EDITOR 309 | /// 310 | /// Draw the point to the active camera using editor handles. 311 | /// 312 | /// Is the shape selected? 313 | /// Is it the active shape? 314 | public override void DrawEditorHandles(bool selected, bool active = false) 315 | { 316 | base.DrawEditorHandles(selected, active); 317 | 318 | /* 319 | Color colorPrev = Handles.color; 320 | Handles.color = colorOutline; 321 | 322 | Vector3[] handlePoints = new Vector3[2]; 323 | 324 | // Draw a + to mark the point 325 | handlePoints[0].x = position.x; 326 | handlePoints[0].y = position.y + penExtent; 327 | handlePoints[1].x = position.x; 328 | handlePoints[1].y = position.y - penExtent; 329 | Handles.DrawAAPolyLine(handleDrawTexture, penSize, handlePoints); 330 | 331 | handlePoints[0].x = position.x + penExtent; 332 | handlePoints[0].y = position.y; 333 | handlePoints[1].x = position.x - penExtent; 334 | handlePoints[1].y = position.y; 335 | Handles.DrawAAPolyLine(handleDrawTexture, penSize, handlePoints); 336 | 337 | Handles.color = colorPrev; 338 | */ 339 | } 340 | 341 | /// 342 | /// Respond to GUI input events in editor. 343 | /// 344 | /// The current event 345 | /// Is it the active shape? 346 | /// Did the shape handle the event? 347 | public override bool HandleEditorEvent(Event currEvent, bool active) 348 | { 349 | return false; 350 | } 351 | #endif 352 | 353 | } -------------------------------------------------------------------------------- /Assets/VectorShapes/PointShape.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d67e408129bd048bfa9ab78aa0553a85 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/PolyShape.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: acffde7e0e46c4d309fb3ada4767ad6e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/SerializedShape.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [CreateAssetMenu(fileName = "Shape", menuName = "Testing/Shape")] 6 | [System.Serializable] 7 | public class SerializedShape : ScriptableObject 8 | { 9 | /// 10 | /// Have to do this crap to use Unity serialization 11 | /// which can't handle class inheritance 12 | /// 13 | [SerializeField] 14 | protected List circles; 15 | 16 | [SerializeField] 17 | protected List ellipses; 18 | 19 | [SerializeField] 20 | protected List points; 21 | 22 | [SerializeField] 23 | protected List polys; 24 | 25 | [SerializeField] 26 | protected List texts; 27 | 28 | [System.NonSerialized] 29 | protected List _components; 30 | public List components 31 | { 32 | get 33 | { 34 | int componentCount = 0; 35 | componentCount += circles.Count; 36 | componentCount += ellipses.Count; 37 | componentCount += points.Count; 38 | componentCount += polys.Count; 39 | componentCount += texts.Count; 40 | 41 | if (_components == null) _components = new List(); 42 | if (_components.Count != componentCount) 43 | { 44 | _components.Clear(); 45 | _components.AddRange(circles); 46 | _components.AddRange(ellipses); 47 | _components.AddRange(points); 48 | _components.AddRange(polys); 49 | _components.AddRange(texts); 50 | } 51 | 52 | return _components; 53 | } 54 | } 55 | 56 | private void OnEnable() 57 | { 58 | if (circles == null) circles = new List(); 59 | if (ellipses == null) ellipses = new List(); 60 | if (points == null) points = new List(); 61 | if (polys == null) polys = new List(); 62 | if (texts == null) texts = new List(); 63 | } 64 | 65 | public void SetShapes(List shapes) 66 | { 67 | circles.Clear(); 68 | ellipses.Clear(); 69 | points.Clear(); 70 | polys.Clear(); 71 | texts.Clear(); 72 | 73 | foreach (VectorShape shape in shapes) 74 | { 75 | AddShape(shape); 76 | } 77 | } 78 | 79 | public void AddShapes(List shapes) 80 | { 81 | foreach (VectorShape shape in shapes) 82 | { 83 | AddShape(shape); 84 | } 85 | } 86 | 87 | public void AddShape(VectorShape shape) 88 | { 89 | if (shape is CircleShape) 90 | { 91 | circles.Add(shape as CircleShape); 92 | } 93 | else if (shape is EllipseShape) 94 | { 95 | ellipses.Add(shape as EllipseShape); 96 | } 97 | else if (shape is PointShape) 98 | { 99 | points.Add(shape as PointShape); 100 | } 101 | else if (shape is PolyShape) 102 | { 103 | polys.Add(shape as PolyShape); 104 | } 105 | else if (shape is TextShape) 106 | { 107 | texts.Add(shape as TextShape); 108 | } 109 | else if (shape is CompoundShape) 110 | { 111 | AddShapes((shape as CompoundShape).components); 112 | } 113 | else 114 | { 115 | Debug.LogWarning("Unknown shape! " + shape); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Assets/VectorShapes/SerializedShape.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c081e07c8a64f48c98e1c02e172f84f0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d1dfb8449dd3c4ec5a427d842960c2c4 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Shader/VectorLineMeshBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using UnityEngine; 5 | using Unity.VectorGraphics; 6 | 7 | public class VectorLineMeshBuilder 8 | { 9 | const int Buffer_Padding = 16; 10 | const int Buffer_Size = 16384 - Buffer_Padding; 11 | 12 | private List vertexList; 13 | private Vector3[] vertexArray; 14 | private int vertexCount; 15 | 16 | private List dataList; 17 | private Vector4[] dataArray; 18 | // dataCount == vertexCount 19 | 20 | private List triangleList; 21 | private int[] triangleArray; 22 | private int triangleCount; 23 | 24 | private int vertexCountOnOpen; 25 | private Vector2 previousDirection; 26 | private float drawingLength; 27 | 28 | public VectorLineMeshBuilder() 29 | { 30 | vertexList = new List(Buffer_Size + Buffer_Padding); 31 | vertexArray = vertexList.GetBackingArray(); 32 | vertexCount = 0; 33 | 34 | dataList = new List(Buffer_Size + Buffer_Padding); 35 | dataArray = dataList.GetBackingArray(); 36 | 37 | triangleList = new List(3 * (Buffer_Size + Buffer_Padding)); 38 | triangleArray = triangleList.GetBackingArray(); 39 | triangleCount = 0; 40 | 41 | vertexCountOnOpen = 0; 42 | drawingLength = 0f; 43 | } 44 | 45 | public void Reset() 46 | { 47 | vertexCount = 0; 48 | triangleCount = 0; 49 | vertexCountOnOpen = 0; 50 | drawingLength = 0f; 51 | } 52 | 53 | /// 54 | /// Open a new section of drawing. 55 | /// 56 | /// Initial vertex position 57 | public void BeginPolyLine(Vector2 point) 58 | { 59 | if (vertexCount > vertexCountOnOpen) 60 | { 61 | Debug.LogError("BeginDrawing called on active drawing"); 62 | return; 63 | } 64 | if (vertexCount > Buffer_Size) 65 | { 66 | Debug.LogWarning("Out of space in buffers"); 67 | return; 68 | } 69 | 70 | vertexCountOnOpen = vertexCount; 71 | 72 | vertexArray[vertexCount++] = point; 73 | vertexArray[vertexCount++] = point; 74 | 75 | previousDirection = Vector2.zero; 76 | drawingLength = 0f; 77 | } 78 | 79 | /// 80 | /// Add a straight segment onto the drawing. 81 | /// 82 | /// New vertex position 83 | public void LineTo(Vector2 point) 84 | { 85 | if (vertexCount <= vertexCountOnOpen) 86 | { 87 | Debug.LogError("LineTo called on inactive drawing"); 88 | return; 89 | } 90 | if (vertexCount > Buffer_Size) 91 | { 92 | Debug.LogWarning("Out of space in buffers"); 93 | return; 94 | } 95 | 96 | int previousVertex = vertexCount - 2; 97 | Vector2 segment = (point - (Vector2)vertexArray[previousVertex]); 98 | Vector2 direction = segment.normalized; 99 | Vector2 offset = Vector2.Perpendicular(direction); 100 | 101 | if (previousVertex == vertexCountOnOpen) // First segment 102 | { 103 | Vector4 data = new Vector4(0, 1, offset.x, offset.y); 104 | dataArray[previousVertex++] = data; 105 | dataArray[previousVertex++] = -data; 106 | 107 | drawingLength += segment.magnitude; 108 | data.x = drawingLength; 109 | 110 | dataArray[vertexCount] = data; 111 | vertexArray[vertexCount++] = point; 112 | dataArray[vertexCount] = -data; 113 | vertexArray[vertexCount++] = point; 114 | } 115 | else 116 | { 117 | Vector4 data; 118 | Vector4 previousData = dataArray[previousVertex]; 119 | Vector2 previousOffset = new Vector2(previousData.z, previousData.w); 120 | 121 | float extent = 1f + Vector2.Dot(offset, previousOffset); 122 | 123 | if (extent < 0.5f) 124 | { 125 | Vector2 joinOffset; 126 | Vector2 midOffset; 127 | 128 | if (extent < Mathf.Epsilon) 129 | { 130 | joinOffset = previousDirection; 131 | } 132 | else 133 | { 134 | joinOffset = (offset + previousOffset).normalized; 135 | } 136 | 137 | extent = 1f + Vector2.Dot(previousOffset, joinOffset); 138 | midOffset = (previousOffset + joinOffset) / extent; 139 | data = new Vector4(drawingLength, 1, midOffset.x, midOffset.y); 140 | dataArray[previousVertex++] = data; 141 | dataArray[previousVertex--] = -data; 142 | 143 | data = new Vector4(drawingLength, 0, 0, 0); 144 | dataArray[vertexCount] = data; 145 | vertexArray[vertexCount++] = vertexArray[previousVertex]; 146 | 147 | // cross product to check which way the corner goes 148 | float sign = -Mathf.Sign(previousDirection.x * direction.y - previousDirection.y * direction.x); 149 | data = new Vector4(drawingLength, sign, sign * joinOffset.x * extent, sign * joinOffset.y * extent); 150 | dataArray[vertexCount] = data; 151 | vertexArray[vertexCount++] = vertexArray[previousVertex]; 152 | 153 | triangleArray[triangleCount++] = vertexCount - 2; 154 | if (sign > 0) 155 | { 156 | triangleArray[triangleCount++] = vertexCount - 4; 157 | triangleArray[triangleCount++] = vertexCount - 1; 158 | } 159 | else 160 | { 161 | triangleArray[triangleCount++] = vertexCount - 1; 162 | triangleArray[triangleCount++] = vertexCount - 3; 163 | } 164 | 165 | midOffset = (offset + joinOffset) / extent; 166 | data = new Vector4(drawingLength, 1, midOffset.x, midOffset.y); 167 | dataArray[vertexCount] = data; 168 | vertexArray[vertexCount++] = vertexArray[previousVertex]; 169 | dataArray[vertexCount] = -data; 170 | vertexArray[vertexCount++] = vertexArray[previousVertex]; 171 | 172 | triangleArray[triangleCount++] = vertexCount - 4; 173 | if (sign > 0) 174 | { 175 | triangleArray[triangleCount++] = vertexCount - 3; 176 | triangleArray[triangleCount++] = vertexCount - 2; 177 | } 178 | else 179 | { 180 | triangleArray[triangleCount++] = vertexCount - 1; 181 | triangleArray[triangleCount++] = vertexCount - 3; 182 | } 183 | } 184 | else 185 | { 186 | Vector2 joinOffset = (offset + previousOffset) / extent; 187 | data = new Vector4(drawingLength, 1, joinOffset.x, joinOffset.y); 188 | dataArray[previousVertex++] = data; 189 | dataArray[previousVertex++] = -data; 190 | } 191 | 192 | drawingLength += segment.magnitude; 193 | 194 | data = new Vector4(drawingLength, 1, offset.x, offset.y); 195 | dataArray[vertexCount] = data; 196 | vertexArray[vertexCount++] = point; 197 | dataArray[vertexCount] = -data; 198 | vertexArray[vertexCount++] = point; 199 | } 200 | 201 | triangleArray[triangleCount++] = vertexCount - 4; 202 | triangleArray[triangleCount++] = vertexCount - 2; 203 | triangleArray[triangleCount++] = vertexCount - 3; 204 | 205 | triangleArray[triangleCount++] = vertexCount - 2; 206 | triangleArray[triangleCount++] = vertexCount - 1; 207 | triangleArray[triangleCount++] = vertexCount - 3; 208 | 209 | previousDirection = direction; 210 | } 211 | 212 | /// 213 | /// Add a quadratic curve segment onto the drawing. 214 | /// 215 | /// Control point position 216 | /// New vertex position 217 | /// Number of segments to include 218 | public void CurveTo(Vector2 control, Vector2 point, int steps) 219 | { 220 | CurveTo(point, control, control, steps); 221 | } 222 | 223 | /// 224 | /// Add a cubic curve segment onto the drawing. 225 | /// 226 | /// Control point A position 227 | /// Control point B position 228 | /// New vertex position 229 | /// Number of segments to include 230 | public void CurveTo(Vector2 controlA, Vector2 controlB, Vector2 point, int steps) 231 | { 232 | if (vertexCount <= vertexCountOnOpen) 233 | { 234 | Debug.LogError("CurveTo called on inactive drawing"); 235 | return; 236 | } 237 | if (vertexCount > Buffer_Size) 238 | { 239 | Debug.LogWarning("Out of space in buffers"); 240 | return; 241 | } 242 | 243 | int previousVertex = vertexCount - 2; 244 | Vector2 previousPoint = vertexArray[previousVertex]; 245 | 246 | BezierSegment bezier = new BezierSegment(); 247 | bezier.P0 = previousPoint; 248 | bezier.P1 = controlA; 249 | bezier.P2 = controlB; 250 | bezier.P3 = point; 251 | 252 | float length = VectorUtils.SegmentLength(bezier) / steps; 253 | 254 | float step = 1f / steps; 255 | float t = step; 256 | 257 | Vector2 bezierPoint = VectorUtils.Eval(bezier, t); 258 | Vector2 tangent = VectorUtils.EvalTangent(bezier, t); 259 | Vector2 offset = Vector2.Perpendicular(tangent); 260 | Vector4 data; 261 | 262 | LineTo(bezierPoint); 263 | 264 | for (int i = 1; i < steps; i++) 265 | { 266 | t += step; 267 | drawingLength += length; // Good enough for our purposes. 268 | 269 | bezierPoint = VectorUtils.EvalFull(bezier, t, out tangent); 270 | offset = Vector2.Perpendicular(tangent); 271 | data = new Vector4(drawingLength, 1, offset.x, offset.y); 272 | dataArray[vertexCount] = data; 273 | vertexArray[vertexCount++] = bezierPoint; 274 | dataArray[vertexCount] = -data; 275 | vertexArray[vertexCount++] = bezierPoint; 276 | 277 | triangleArray[triangleCount++] = vertexCount - 4; 278 | triangleArray[triangleCount++] = vertexCount - 2; 279 | triangleArray[triangleCount++] = vertexCount - 3; 280 | 281 | triangleArray[triangleCount++] = vertexCount - 2; 282 | triangleArray[triangleCount++] = vertexCount - 1; 283 | triangleArray[triangleCount++] = vertexCount - 3; 284 | } 285 | 286 | previousDirection = tangent; 287 | } 288 | 289 | public void EndPolyLine(bool closed = false) 290 | { 291 | if (closed) 292 | { 293 | } 294 | else if (vertexCount > vertexCountOnOpen) 295 | { 296 | Vector2 offset = Vector2.Perpendicular(previousDirection); 297 | Vector4 data = new Vector4(drawingLength, 1, offset.x, offset.y); 298 | dataArray[vertexCount - 2] = data; 299 | dataArray[vertexCount - 1] = -data; 300 | } 301 | 302 | vertexCountOnOpen = vertexCount; 303 | } 304 | 305 | public void Circle(Vector2 center, float radius, int steps) 306 | { 307 | if (vertexCount > vertexCountOnOpen) 308 | { 309 | Debug.LogError("Circle called on active drawing"); 310 | return; 311 | } 312 | if ((vertexCount + steps) > Buffer_Size) 313 | { 314 | Debug.LogWarning("Out of space in buffers"); 315 | return; 316 | } 317 | 318 | vertexCountOnOpen = vertexCount; 319 | 320 | int segments = Mathf.CeilToInt(steps / 4f); 321 | float angle = 0f; 322 | float angleDelta = Mathf.PI / (2 * segments); 323 | Vector2[] offsets = new Vector2[segments]; 324 | for (int i = 0; i < segments; i++) 325 | { 326 | offsets[i] = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)); 327 | angle += angleDelta; 328 | } 329 | 330 | Vector2 offset; 331 | Vector2 point; 332 | Vector4 data; 333 | 334 | float length = angleDelta * radius; 335 | for (int quadrant = 0; quadrant < 4; quadrant++) 336 | { 337 | for (int i = 0; i < segments; i++) 338 | { 339 | offset = offsets[i]; 340 | point = center + offset * radius; 341 | data = new Vector4(drawingLength, 1, -offset.x, -offset.y); 342 | 343 | dataArray[vertexCount] = data; 344 | vertexArray[vertexCount++] = point; 345 | dataArray[vertexCount] = -data; 346 | vertexArray[vertexCount++] = point; 347 | 348 | // This is ugly, but we'll clean it up after the loop 349 | triangleArray[triangleCount++] = vertexCount - 2; 350 | triangleArray[triangleCount++] = vertexCount - 0; 351 | triangleArray[triangleCount++] = vertexCount - 1; 352 | 353 | triangleArray[triangleCount++] = vertexCount - 0; 354 | triangleArray[triangleCount++] = vertexCount + 1; 355 | triangleArray[triangleCount++] = vertexCount - 1; 356 | 357 | drawingLength += length; 358 | 359 | offsets[i].x = -offset.y; 360 | offsets[i].y = offset.x; 361 | } 362 | } 363 | 364 | // Fix the out of bounds triangles by repeating first point 365 | offset = offsets[0]; 366 | point = center + offset * radius; 367 | data = new Vector4(drawingLength, 1, -offset.x, -offset.y); 368 | 369 | dataArray[vertexCount] = data; 370 | vertexArray[vertexCount++] = point; 371 | dataArray[vertexCount] = -data; 372 | vertexArray[vertexCount++] = point; 373 | 374 | vertexCountOnOpen = vertexCount; 375 | } 376 | 377 | public Mesh GetMesh() 378 | { 379 | Mesh mesh = new Mesh(); 380 | 381 | vertexList.SetActiveSize(vertexCount); 382 | mesh.SetVertices(vertexList); 383 | 384 | dataList.SetActiveSize(vertexCount); 385 | mesh.SetUVs(0, dataList); 386 | 387 | triangleList.SetActiveSize(triangleCount); 388 | mesh.SetTriangles(triangleList, 0); 389 | 390 | mesh.UploadMeshData(false); 391 | return mesh; 392 | } 393 | } 394 | 395 | // Extension class for System.Collections.Generic.List to get 396 | // its backing array field via reflection. 397 | // Author: Jackson Dunstan, http://JacksonDunstan.com/articles/3066 398 | public static class ListBackingArrayGetter 399 | { 400 | // Name of the backing array field 401 | private const string FieldName = "_items"; 402 | 403 | // Flags passed to Type.GetField to get the backing array field 404 | private const BindingFlags GetFieldFlags = BindingFlags.NonPublic | BindingFlags.Instance; 405 | 406 | // Cached backing array FieldInfo instances per Type 407 | private static readonly Dictionary itemsFields = new Dictionary(); 408 | 409 | // Get a List's backing array 410 | public static TElement[] GetBackingArray(this List list) 411 | { 412 | // Check if the FieldInfo is already in the cache 413 | var listType = typeof(List); 414 | FieldInfo fieldInfo; 415 | if (!itemsFields.TryGetValue(listType, out fieldInfo)) 416 | { 417 | // Generate the FieldInfo and add it to the cache 418 | fieldInfo = listType.GetField(FieldName, GetFieldFlags); 419 | itemsFields.Add(listType, fieldInfo); 420 | } 421 | 422 | // Get the backing array of the given List 423 | var items = (TElement[])fieldInfo.GetValue(list); 424 | return items; 425 | } 426 | } 427 | 428 | // Extension class for System.Collections.Generic.List to set 429 | // the value of its active size field via reflection. 430 | public static class ListSizeSetter 431 | { 432 | // Name of the size field 433 | private const string FieldName = "_size"; 434 | 435 | // Flags passed to Type.GetField to get the size field 436 | private const BindingFlags GetFieldFlags = BindingFlags.NonPublic | BindingFlags.Instance; 437 | 438 | // Cached backing array FieldInfo instances per Type 439 | private static readonly Dictionary itemsFields = new Dictionary(); 440 | 441 | // Set a List's active size 442 | public static void SetActiveSize(this List list, int size) 443 | { 444 | // Check if the FieldInfo is already in the cache 445 | var listType = typeof(List); 446 | FieldInfo fieldInfo; 447 | if (!itemsFields.TryGetValue(listType, out fieldInfo)) 448 | { 449 | // Generate the FieldInfo and add it to the cache 450 | fieldInfo = listType.GetField(FieldName, GetFieldFlags); 451 | itemsFields.Add(listType, fieldInfo); 452 | } 453 | 454 | // Set the active size of the given List 455 | int newSize = size; 456 | if (newSize < 0) newSize = 0; 457 | if (newSize > list.Capacity) newSize = list.Capacity; 458 | 459 | fieldInfo.SetValue(list, newSize); 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Shader/VectorLineMeshBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1dbcd4befa9264c1492ebe8a6cb52079 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Shader/VectorLineShader.shader: -------------------------------------------------------------------------------- 1 | // Roughly based on 2 | // https://blog.mapbox.com/drawing-antialiased-lines-with-opengl-8766f34192dc 3 | // and 4 | // https://mattdesl.svbtle.com/drawing-lines-is-hard 5 | Shader "Unlit/VectorLineShader" 6 | { 7 | Properties 8 | { 9 | _LineColor ("Line Color", Color) = (0, 0, 0, 1) 10 | _LineWidth ("Line Width", Float) = 1 11 | _Feather ("Feather", Range (0, 1)) = 0.25 12 | _PixelScale ("Pixel Scale", Float) = 0.1 13 | 14 | _StripeColor ("Stripe Color", Color) = (1, 1, 1, 1) 15 | _StripeGap ("Stripe Gap", Float) = 10 16 | _StripeWidth ("Stripe Width", Float) = 1 17 | } 18 | SubShader 19 | { 20 | Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" } 21 | Blend SrcAlpha OneMinusSrcAlpha 22 | ZWrite Off 23 | LOD 100 24 | 25 | Pass 26 | { 27 | CGPROGRAM 28 | #pragma vertex vert 29 | #pragma fragment frag 30 | 31 | #pragma multi_compile STRIPES_OFF STRIPES_ON 32 | 33 | #include "UnityCG.cginc" 34 | 35 | struct appdata 36 | { 37 | float4 vertex : POSITION; 38 | float4 normal : TEXCOORD0; 39 | }; 40 | 41 | struct v2f 42 | { 43 | float4 vertex : SV_POSITION; 44 | half2 offset : TEXCOORD0; 45 | }; 46 | 47 | fixed4 _LineColor; 48 | half _LineWidth; 49 | half _Feather; 50 | half _PixelScale; 51 | 52 | fixed4 _StripeColor; 53 | half _StripeWidth; 54 | half _StripeGap; 55 | 56 | static half _StripeLength = _StripeWidth + _StripeGap; 57 | 58 | v2f vert (appdata v) 59 | { 60 | v2f o; 61 | // UnityObjectToWorldDir without the normalize 62 | float3 normal = mul((float3x3)unity_ObjectToWorld, float3(v.normal.zw, 0.0)); 63 | float3 pos = UnityObjectToViewPos(v.vertex); 64 | float3 delta = normal * _LineWidth * _PixelScale; 65 | o.vertex = UnityViewToClipPos(pos + delta); 66 | o.offset.x = abs(v.normal.x); 67 | o.offset.y = v.normal.y; 68 | 69 | return o; 70 | } 71 | 72 | fixed4 frag (v2f f) : SV_Target 73 | { 74 | #if STRIPES_ON 75 | float distX = fmod(f.offset.x, _StripeLength); 76 | float stripe = saturate(abs((_StripeGap - distX) / _StripeWidth) * 2.0); 77 | float4 color = lerp(_StripeColor, _LineColor, stripe); 78 | #else 79 | float4 color = _LineColor; 80 | #endif 81 | 82 | float distY = abs(f.offset.y); 83 | float alpha = saturate((1.0 - distY) / _Feather); 84 | 85 | return float4(color.rgb, alpha * color.a); 86 | } 87 | ENDCG 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Assets/VectorShapes/Shader/VectorLineShader.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 100745e9c99b34cda851eeb515eedc76 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/VectorShapes/TextShape.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml; 3 | using UnityEngine; 4 | #if UNITY_EDITOR 5 | using UnityEditor; 6 | #endif 7 | using Unity.VectorGraphics; 8 | 9 | [System.Serializable] 10 | /// 11 | /// Vector point. 12 | /// 13 | public class TextShape : VectorShape 14 | { 15 | /// 16 | /// Position of text origin. 17 | /// 18 | public Vector2 position; 19 | 20 | /// 21 | /// Rotation of text (in degrees). 22 | /// 23 | public float rotation; 24 | 25 | /// 26 | /// Text. 27 | /// 28 | public string text; 29 | 30 | /// 31 | /// Font. 32 | /// 33 | [SerializeField] 34 | protected string font; 35 | 36 | /// 37 | /// Font size. 38 | /// 39 | [SerializeField] 40 | protected int size; 41 | 42 | protected TextShape() 43 | { 44 | } 45 | 46 | /// 47 | /// New point from location. 48 | /// 49 | /// Position of point 50 | /// Text to display 51 | public TextShape(Vector2 location, string content) 52 | { 53 | position = location; 54 | text = content; 55 | } 56 | 57 | public static TextShape Create() 58 | { 59 | //TextShape shape = ScriptableObject.CreateInstance(); 60 | TextShape shape = new TextShape(); 61 | 62 | return shape; 63 | } 64 | 65 | /// 66 | /// New point from location. 67 | /// 68 | /// Position of point 69 | /// Text to display 70 | public static TextShape Create(Vector2 location, string content) 71 | { 72 | TextShape shape = Create(); 73 | 74 | shape.position = location; 75 | shape.text = content; 76 | 77 | return shape; 78 | } 79 | 80 | /// 81 | /// Copy of the shape. 82 | /// 83 | /// New shape with properties of existing shape 84 | public override VectorShape Duplicate() 85 | { 86 | return Create(position, text); 87 | } 88 | 89 | /// 90 | /// Distance between a point and the shape. 91 | /// 92 | /// Test point 93 | /// Distance from point to nearest point on shape 94 | public override float Distance(Vector2 pt) 95 | { 96 | return Vector2.Distance(pt, position); 97 | } 98 | 99 | /// 100 | /// Tests if a shape contains a point 101 | /// 102 | /// Test point 103 | /// Is the point inside the shape? 104 | public override bool Contains(Vector2 pt) 105 | { 106 | return false; 107 | } 108 | 109 | /// 110 | /// Tests if a shape is inside a rectangle. 111 | /// 112 | /// Test rectangle 113 | /// Is the shape entirely inside the rectangle? 114 | public override bool IsInside(Rect rect) 115 | { 116 | return rect.Contains(position); 117 | } 118 | 119 | /// 120 | /// Rotate the shape around a point. 121 | /// 122 | /// Center of rotation 123 | /// Angle in degrees 124 | public override void RotateAround(Vector2 center, float angle) 125 | { 126 | Matrix2D matrix = Matrix2D.Translate(center) * Matrix2D.RotateRH(angle * Mathf.Deg2Rad) * Matrix2D.Translate(-center); 127 | position = matrix.MultiplyPoint(position); 128 | 129 | Dirty = true; 130 | } 131 | 132 | /// 133 | /// Change the origin of the shape. 134 | /// 135 | /// Direction to move 136 | public override void TranslateBy(Vector2 offset) 137 | { 138 | position += offset; 139 | 140 | Dirty = true; 141 | } 142 | 143 | /// 144 | /// Change the size of the shape. 145 | /// 146 | /// Scaling factor to apply 147 | public override void ScaleBy(float scale) 148 | { 149 | if (scale < Mathf.Epsilon) 150 | { 151 | Debug.LogWarning("Scale must be greater than zero."); 152 | return; 153 | } 154 | 155 | position *= scale; 156 | 157 | Dirty = true; 158 | } 159 | 160 | /// 161 | /// Transform the shape by an arbitrary matrix. 162 | /// 163 | /// Matrix to transform shape 164 | public override void TransformBy(Matrix2D matrix) 165 | { 166 | position = matrix.MultiplyPoint(position); 167 | 168 | Dirty = true; 169 | } 170 | 171 | /// 172 | /// Distance between a point and the shape. 173 | /// 174 | /// Test point 175 | /// Snap modes to consider 176 | /// Distance from point to nearest point on shape 177 | public override SnapPoint GetSnap(Vector2 pt, SnapPoint.Mode mode) 178 | { 179 | SnapPoint snap = new SnapPoint(); 180 | 181 | return snap; 182 | } 183 | 184 | /// 185 | /// Tessellate the shape into geometry data. 186 | /// 187 | protected override void GenerateGeometry() 188 | { 189 | if ((shapeGeometry != null) && (!shapeDirty)) return; 190 | 191 | PathProperties pathProps = new PathProperties() 192 | { 193 | Stroke = new Stroke() 194 | { 195 | Color = colorOutline, 196 | HalfThickness = penSize / 2f * penToMeshScale 197 | } 198 | }; 199 | 200 | Shape textShape = new Shape() 201 | { 202 | Contours = new BezierContour[] 203 | { 204 | }, 205 | PathProps = pathProps 206 | }; 207 | 208 | shapeNode = new SceneNode() 209 | { 210 | Transform = matrixTransform, 211 | Shapes = new List 212 | { 213 | textShape 214 | } 215 | }; 216 | 217 | tessellationScene.Root = shapeNode; 218 | 219 | shapeGeometry = VectorUtils.TessellateScene(tessellationScene, tessellationOptions); 220 | shapeDirty = false; 221 | } 222 | 223 | /// 224 | /// Build a mesh for display with the VectorLineShader. 225 | /// 226 | protected override void GenerateLineMesh() 227 | { 228 | } 229 | 230 | /// 231 | /// Build a 2D bounding box for the shape. 232 | /// 233 | protected override void GenerateBounds() 234 | { 235 | shapeBounds = new Rect(position, Vector2.zero); 236 | boundsDirty = false; 237 | } 238 | 239 | /// 240 | /// Build a 2D collider for the shape. 241 | /// 242 | protected override void AddColliderToGO(GameObject target) 243 | { 244 | CircleCollider2D[] colliders = target.GetComponents(); 245 | CircleCollider2D collider = null; 246 | 247 | for (int i = 0; i < colliders.Length; i++) 248 | { 249 | if (colliders[i].name == this.guid) 250 | { 251 | collider = colliders[i]; 252 | } 253 | } 254 | 255 | if (collider == null) 256 | { 257 | collider = collider.gameObject.AddComponent(); 258 | collider.name = this.guid; 259 | } 260 | 261 | collider.offset = position; 262 | } 263 | 264 | /// 265 | /// Serialize the shape to an XML writer. 266 | /// 267 | public override void WriteToXML(XmlWriter writer, Vector2 origin, float scale) 268 | { 269 | } 270 | 271 | #if UNITY_EDITOR 272 | /// 273 | /// Draw the point to the active camera using editor handles. 274 | /// 275 | /// Is the shape selected? 276 | /// Is it the active shape? 277 | public override void DrawEditorHandles(bool selected, bool active = false) 278 | { 279 | base.DrawEditorHandles(selected, active); 280 | 281 | /* 282 | Color colorPrev = Handles.color; 283 | Handles.color = colorOutline; 284 | 285 | Vector3[] handlePoints = new Vector3[2]; 286 | 287 | // Draw a + to mark the point 288 | handlePoints[0].x = position.x; 289 | handlePoints[0].y = position.y + penExtent; 290 | handlePoints[1].x = position.x; 291 | handlePoints[1].y = position.y - penExtent; 292 | Handles.DrawAAPolyLine(handleDrawTexture, penSize, handlePoints); 293 | 294 | handlePoints[0].x = position.x + penExtent; 295 | handlePoints[0].y = position.y; 296 | handlePoints[1].x = position.x - penExtent; 297 | handlePoints[1].y = position.y; 298 | Handles.DrawAAPolyLine(handleDrawTexture, penSize, handlePoints); 299 | 300 | Handles.color = colorPrev; 301 | */ 302 | } 303 | 304 | /// 305 | /// Respond to GUI input events in editor. 306 | /// 307 | /// The current event 308 | /// Is it the active shape? 309 | /// Did the shape handle the event? 310 | public override bool HandleEditorEvent(Event currEvent, bool active) 311 | { 312 | return false; 313 | } 314 | #endif 315 | 316 | } -------------------------------------------------------------------------------- /Assets/VectorShapes/TextShape.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1b42e1e547752419fbe335165fdba8a8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShape.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml; 3 | using UnityEngine; 4 | using Unity.VectorGraphics; 5 | 6 | /// 7 | /// Base class of drawable vector based 2d elements. 8 | /// 9 | public abstract class VectorShape : System.IDisposable 10 | { 11 | public class ShapeProxy : ScriptableObject, ISerializationCallbackReceiver 12 | { 13 | [HideInInspector] 14 | public VectorShape shape; 15 | 16 | [Range(0f, 25f)] 17 | public float penSize; 18 | 19 | public Color32 colorOutline; 20 | public Color32 colorFill; 21 | 22 | public void OnBeforeSerialize() 23 | { 24 | if (shape != null) 25 | { 26 | shape.penSize = penSize; 27 | shape.colorOutline = colorOutline; 28 | shape.colorFill = colorFill; 29 | } 30 | } 31 | 32 | public void OnAfterDeserialize() 33 | { 34 | } 35 | } 36 | 37 | public ShapeProxy GetShapeProxy() 38 | { 39 | ShapeProxy proxy = ScriptableObject.CreateInstance(); 40 | proxy.name = "Shape"; 41 | proxy.shape = this; 42 | proxy.penSize = penSize; 43 | proxy.colorOutline = colorOutline; 44 | proxy.colorFill = colorFill; 45 | 46 | return proxy; 47 | } 48 | 49 | public class SnapPoint 50 | { 51 | [System.Flags] 52 | public enum Mode 53 | { 54 | None = 0, 55 | Center = 1, 56 | Endpoint = 2, 57 | Midpoint = 4, 58 | Edge = 8 59 | } 60 | public Mode mode = Mode.None; 61 | 62 | public Vector2 point; 63 | } 64 | 65 | /// 66 | /// Pen size for drawing. 67 | /// 68 | [Range(0f, 25f)] 69 | public float penSize = 2f; 70 | 71 | /// 72 | /// Scale between pen and mesh units. 73 | /// 74 | public float penToMeshScale = 0.01f; 75 | 76 | /// 77 | /// Outline color. 78 | /// 79 | public Color colorOutline = Color.black; 80 | 81 | /// 82 | /// Fill color. 83 | /// 84 | public Color colorFill = Color.clear; 85 | 86 | /// 87 | /// Does the last vertex connect back to the first? 88 | /// 89 | [SerializeField] 90 | protected bool closed = true; 91 | 92 | /// 93 | /// ID. 94 | /// 95 | [SerializeField] 96 | protected string guid = new System.Guid().ToString(); 97 | 98 | /// 99 | /// Transform matrix. 100 | /// 101 | [SerializeField] 102 | protected Matrix2D matrixTransform = Matrix2D.identity; 103 | 104 | /// 105 | /// Geometry data generated for shape. 106 | /// 107 | protected List shapeGeometry = null; 108 | 109 | /// 110 | /// Scene data generated for shape. 111 | /// 112 | protected SceneNode shapeNode = null; 113 | 114 | /// 115 | /// Mesh generated for tesselated shape. 116 | /// 117 | protected Mesh shapeMesh = null; 118 | 119 | /// 120 | /// Line renderer mesh for shape outline. 121 | /// 122 | protected Mesh lineMesh = null; 123 | 124 | /// 125 | /// Bounds rectangle for shape (may be approximate for some shapes). 126 | /// 127 | protected Rect shapeBounds = Rect.zero; 128 | 129 | protected bool shapeDirty = true; 130 | protected bool lineDirty = true; 131 | protected bool boundsDirty = true; 132 | 133 | /// 134 | /// Level To which the shape selected. 135 | /// 136 | public enum SelectionLevel 137 | { 138 | None, 139 | Shape, 140 | Component, 141 | Vertex 142 | } 143 | 144 | #if UNITY_EDITOR 145 | /// 146 | /// Shared texture for drawing AA lines in the editor. 147 | /// 148 | protected static Texture2D handleDrawTexture; 149 | 150 | /// 151 | /// Scale for drawing handles in the editor. 152 | /// 153 | public static float handleDrawSize = 0f; 154 | 155 | /// 156 | /// Color of a vertex handle 157 | /// 158 | public static Color vertexHandleColor = Color.red; 159 | 160 | /// 161 | /// Color of a control handle 162 | /// 163 | public static Color controlHandleColor = Color.red; 164 | #endif 165 | 166 | /// 167 | /// Shared scene for tessellating shape meshes. 168 | /// 169 | protected static Scene tessellationScene; 170 | 171 | /// 172 | /// Shared settings for tessellating shape meshes. 173 | /// 174 | public static VectorUtils.TessellationOptions tessellationOptions; 175 | 176 | /// 177 | /// Shared builder for making line meshes. 178 | /// 179 | public static VectorLineMeshBuilder lineBuilder; 180 | 181 | static VectorShape() 182 | { 183 | tessellationScene = new Scene(); 184 | 185 | tessellationOptions = new VectorUtils.TessellationOptions() 186 | { 187 | StepDistance = 50f, 188 | MaxCordDeviation = float.MaxValue, 189 | MaxTanAngleDeviation = Mathf.PI / 16.0f, 190 | SamplingStepSize = 0.05f 191 | }; 192 | 193 | lineBuilder = new VectorLineMeshBuilder(); 194 | } 195 | 196 | /// 197 | /// Dispose of the shape mesh. 198 | /// 199 | public void Dispose() 200 | { 201 | if (shapeMesh != null) 202 | { 203 | #if UNITY_EDITOR 204 | UnityEngine.Object.DestroyImmediate(shapeMesh); 205 | #else 206 | UnityEngine.Object.Destroy(shapeMesh); 207 | #endif 208 | } 209 | 210 | shapeDirty = true; 211 | shapeMesh = null; 212 | 213 | if (lineMesh != null) 214 | { 215 | #if UNITY_EDITOR 216 | UnityEngine.Object.DestroyImmediate(lineMesh); 217 | #else 218 | UnityEngine.Object.Destroy(lineMesh); 219 | #endif 220 | } 221 | 222 | lineDirty = true; 223 | lineMesh = null; 224 | } 225 | 226 | /// 227 | /// Mesh built from the tesselated shape. 228 | /// 229 | public Mesh ShapeMesh 230 | { 231 | get 232 | { 233 | if ((shapeMesh == null) || shapeDirty) 234 | { 235 | if (shapeMesh != null) 236 | { 237 | #if UNITY_EDITOR 238 | UnityEngine.Object.DestroyImmediate(shapeMesh); 239 | #else 240 | UnityEngine.Object.Destroy(shapeMesh); 241 | #endif 242 | } 243 | 244 | GenerateGeometry(); 245 | 246 | shapeMesh = new Mesh(); 247 | if (shapeGeometry != null) 248 | { 249 | VectorUtils.FillMesh(shapeMesh, shapeGeometry, 1.0f); 250 | } 251 | shapeDirty = false; 252 | } 253 | 254 | return shapeMesh; 255 | } 256 | } 257 | 258 | /// 259 | /// Mesh of the shape outline for VectorLineShader. 260 | /// 261 | public Mesh LineMesh 262 | { 263 | get 264 | { 265 | if ((lineMesh == null) || lineDirty) 266 | { 267 | if (lineMesh != null) 268 | { 269 | #if UNITY_EDITOR 270 | UnityEngine.Object.DestroyImmediate(lineMesh); 271 | #else 272 | UnityEngine.Object.Destroy(lineMesh); 273 | #endif 274 | } 275 | 276 | lineBuilder.Reset(); 277 | 278 | GenerateLineMesh(); 279 | 280 | lineMesh = lineBuilder.GetMesh(); 281 | lineDirty = false; 282 | } 283 | 284 | return lineMesh; 285 | } 286 | } 287 | 288 | /// 289 | /// Mesh of the filled shape (may be empty). 290 | /// 291 | public Mesh FillMesh 292 | { 293 | get 294 | { 295 | return null; 296 | } 297 | } 298 | 299 | /// 300 | /// 2D bounding box of the shape. 301 | /// 302 | public Rect ShapeBounds 303 | { 304 | get 305 | { 306 | if (boundsDirty) 307 | { 308 | GenerateBounds(); 309 | } 310 | 311 | return shapeBounds; 312 | } 313 | } 314 | 315 | /// 316 | /// Tessellated geometry of the shape. 317 | /// 318 | public List ShapeGeometry 319 | { 320 | get 321 | { 322 | if ((shapeGeometry == null) || shapeDirty) 323 | { 324 | GenerateGeometry(); 325 | } 326 | 327 | return shapeGeometry; 328 | } 329 | } 330 | 331 | /// 332 | /// Is the shape dirty? 333 | /// 334 | public bool Dirty 335 | { 336 | set 337 | { 338 | if (value) 339 | { 340 | shapeDirty = true; 341 | lineDirty = true; 342 | boundsDirty = true; 343 | } 344 | } 345 | get 346 | { 347 | return shapeDirty; 348 | } 349 | } 350 | 351 | /// 352 | /// Is the shape closed? 353 | /// 354 | public bool Closed 355 | { 356 | set 357 | { 358 | closed = value; 359 | } 360 | get 361 | { 362 | return closed; 363 | } 364 | } 365 | 366 | /// 367 | /// ID of the shape 368 | /// 369 | public string ID 370 | { 371 | set 372 | { 373 | guid = value; 374 | } 375 | get 376 | { 377 | return guid; 378 | } 379 | } 380 | 381 | /// 382 | /// Copy of the shape. 383 | /// 384 | /// New shape with properties of existing shape 385 | public abstract VectorShape Duplicate(); 386 | 387 | /// 388 | /// Distance between a point and the shape. 389 | /// 390 | /// Test point 391 | /// Distance from point to nearest point on shape 392 | public abstract float Distance(Vector2 pt); 393 | 394 | /// 395 | /// Tests if a shape contains a point. 396 | /// 397 | /// Test point 398 | /// Is the point inside the shape? 399 | public abstract bool Contains(Vector2 pt); 400 | 401 | /// 402 | /// Tests if a shape is inside a rectangle. 403 | /// 404 | /// Test rectangle 405 | /// Is the shape entirely inside the rectangle? 406 | public abstract bool IsInside(Rect rect); 407 | 408 | /// 409 | /// Rotate the shape around a point. 410 | /// 411 | /// Center of rotation 412 | /// Angle in degrees 413 | public abstract void RotateAround(Vector2 center, float angle); 414 | 415 | /// 416 | /// Change the origin of the shape. 417 | /// 418 | /// Offset to move shape 419 | public abstract void TranslateBy(Vector2 offset); 420 | 421 | /// 422 | /// Change the size of the shape. 423 | /// 424 | /// Scaling factor to apply 425 | public abstract void ScaleBy(float scale); 426 | 427 | /// 428 | /// Transform the shape by an arbitrary matrix. 429 | /// 430 | /// Matrix to transform shape 431 | public abstract void TransformBy(Matrix2D matrix); 432 | 433 | /// 434 | /// Build a mesh for display with the VectorLineShader. 435 | /// 436 | protected abstract void GenerateLineMesh(); 437 | 438 | /// 439 | /// Distance between a point and the shape. 440 | /// 441 | /// Test point 442 | /// Snap modes to consider 443 | /// Distance from point to nearest point on shape 444 | public abstract SnapPoint GetSnap(Vector2 pt, SnapPoint.Mode mode); 445 | 446 | /// 447 | /// Tessellate the shape into geometry data. 448 | /// 449 | protected abstract void GenerateGeometry(); 450 | 451 | /// 452 | /// Build a 2D bounding box for the shape. 453 | /// 454 | protected abstract void GenerateBounds(); 455 | 456 | /// 457 | /// Add a 2D collider for the shape to a game object. 458 | /// 459 | protected abstract void AddColliderToGO(GameObject target); 460 | 461 | /// 462 | /// Serialize the shape to an XML writer. 463 | /// 464 | public abstract void WriteToXML(XmlWriter writer, Vector2 origin, float scale); 465 | 466 | #if UNITY_EDITOR 467 | /// 468 | /// Draw the shape to the active camera using editor handles. 469 | /// 470 | /// Is the shape selected? 471 | /// Is it the active shape? 472 | public virtual void DrawEditorHandles(bool selected, bool active = false) 473 | { 474 | if (handleDrawTexture == null) 475 | { 476 | handleDrawTexture = new Texture2D(1, 2); 477 | handleDrawTexture.SetPixel(0, 0, Color.white); 478 | handleDrawTexture.SetPixel(0, 1, Color.clear); 479 | handleDrawTexture.Apply(); 480 | } 481 | } 482 | 483 | /// 484 | /// Respond to GUI input events in editor. 485 | /// 486 | /// The current event 487 | /// Is it the active shape? 488 | /// Did the shape handle the event? 489 | public abstract bool HandleEditorEvent(Event currEvent, bool active); 490 | 491 | /// 492 | /// Calculate distance from GUI ray to shape. 493 | /// 494 | /// Distance from ray in screen space 495 | //public abstract float DistanceFromRay(); 496 | #endif 497 | 498 | } -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShape.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1f56a792f6c504498a8d25fc73c48892 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShapeFIlesDXF.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Unity.VectorGraphics; 5 | 6 | #if DXF_SUPPORT 7 | 8 | using IxMilia.Dxf; 9 | using IxMilia.Dxf.Entities; 10 | 11 | public static class VectorShapeFilesDXF 12 | { 13 | static float dxfScale = 1f; 14 | public static Vector2 ConvertPoint(DxfPoint pt) 15 | { 16 | return new Vector2((float)pt.X * dxfScale, (float)pt.Y * dxfScale); 17 | } 18 | public static Vector2 ConvertPoint(double ptX, double ptY) 19 | { 20 | return new Vector2((float)ptX * dxfScale, (float)ptY * dxfScale); 21 | } 22 | public static Vector2 ConvertVector(DxfVector vector) 23 | { 24 | return new Vector2((float)vector.X * dxfScale, (float)vector.Y * dxfScale); 25 | } 26 | 27 | public static Color32 ConvertColor(DxfColor color) 28 | { 29 | int intColor = 0; 30 | try 31 | { 32 | color.ToRGB(); 33 | } 34 | catch (System.NotSupportedException) 35 | { 36 | } 37 | 38 | byte colorRed = (byte)((intColor >> 16) & 0xFF); 39 | byte colorGreen = (byte)((intColor >> 8) & 0xFF); 40 | byte colorBlue = (byte)((intColor >> 0) & 0xFF); 41 | return new Color32(colorRed, colorGreen, colorBlue, 255); 42 | } 43 | 44 | /// 45 | /// Parse DXF into VectorShape list. 46 | /// 47 | public static List ReadDXF(Stream dxfStream) 48 | { 49 | List shapes = new List(); 50 | 51 | DxfFile dxfFile = DxfFile.Load(dxfStream); 52 | 53 | Dictionary layerColors = new Dictionary(); 54 | foreach (DxfLayer layer in dxfFile.Layers) 55 | { 56 | layerColors.Add(layer.Name, ConvertColor(layer.Color)); 57 | } 58 | 59 | foreach (DxfEntity entity in dxfFile.Entities) 60 | { 61 | VectorShape shape = null; 62 | 63 | switch (entity.EntityType) 64 | { 65 | case DxfEntityType.Point: 66 | DxfModelPoint point = entity as DxfModelPoint; 67 | shape = new PointShape(ConvertPoint(point.Location)); 68 | break; 69 | 70 | case DxfEntityType.Line: 71 | DxfLine line = entity as DxfLine; 72 | Vector2[] endpoints = new Vector2[2]; 73 | endpoints[0] = ConvertPoint(line.P1); 74 | endpoints[1] = ConvertPoint(line.P2); 75 | shape = new PolyShape(endpoints); 76 | break; 77 | 78 | case DxfEntityType.Spline: 79 | DxfSpline spline = entity as DxfSpline; 80 | if ((spline.NumberOfControlPoints % spline.DegreeOfCurve) != 1) 81 | { 82 | Debug.LogError("Invalid spline data! Wrong number of points. " + spline); 83 | break; 84 | } 85 | 86 | Vector2[] controlPoints = new Vector2[spline.NumberOfControlPoints]; 87 | for (int i = 0; i < controlPoints.Length; i++) 88 | { 89 | controlPoints[i] = ConvertPoint(spline.ControlPoints[i]); 90 | } 91 | shape = new PolyShape(controlPoints[0]); 92 | PolyShape shapeSpline = shape as PolyShape; 93 | 94 | switch (spline.DegreeOfCurve) 95 | { 96 | case 1: 97 | 98 | for (int i = 1; i < controlPoints.Length; i++) 99 | { 100 | shapeSpline.LineTo(controlPoints[i]); 101 | } 102 | break; 103 | 104 | case 2: 105 | for (int i = 1; i < controlPoints.Length; i += 2) 106 | { 107 | shapeSpline.CurveTo(controlPoints[i + 1], controlPoints[i]); 108 | } 109 | break; 110 | 111 | case 3: 112 | for (int i = 1; i < controlPoints.Length; i += 3) 113 | { 114 | shapeSpline.CurveTo(controlPoints[i + 2], controlPoints[i], controlPoints[i + 1]); 115 | } 116 | break; 117 | 118 | default: 119 | Debug.LogWarning("Spline with unsupported curve of degree: " + spline.DegreeOfCurve); 120 | break; 121 | } 122 | break; 123 | 124 | case DxfEntityType.Arc: 125 | DxfArc arc = entity as DxfArc; 126 | // If the arc is a complete circle just make one of those 127 | float startAngle = (float)arc.StartAngle; 128 | while (startAngle < 0f) 129 | { 130 | startAngle += 360f; 131 | } 132 | float endAngle = (float)arc.EndAngle; 133 | while (endAngle < startAngle) 134 | { 135 | endAngle += 360f; 136 | } 137 | 138 | float sweep = endAngle - startAngle; 139 | shape = new CircleShape(ConvertPoint(arc.Center), (float)arc.Radius, startAngle, sweep); 140 | break; 141 | 142 | case DxfEntityType.Circle: 143 | DxfCircle circle = entity as DxfCircle; 144 | shape = new CircleShape(ConvertPoint(circle.Center), (float)circle.Radius * dxfScale); 145 | break; 146 | 147 | case DxfEntityType.Ellipse: 148 | DxfEllipse ellipse = entity as DxfEllipse; 149 | // If the ellipse is actually a circle just make one of those 150 | if (Mathf.Approximately((float)ellipse.MinorAxisRatio, 1f)) 151 | { 152 | shape = new CircleShape(ConvertPoint(ellipse.Center), (float)ellipse.MajorAxis.Length * dxfScale); 153 | } 154 | else 155 | { 156 | shape = new EllipseShape(ConvertPoint(ellipse.Center), ConvertVector(ellipse.MajorAxis), (float)ellipse.MinorAxisRatio); 157 | } 158 | break; 159 | 160 | case DxfEntityType.Polyline: 161 | DxfPolyline polyline = entity as DxfPolyline; 162 | if (polyline.ContainsVertices) 163 | { 164 | Vector2[] vertices = new Vector2[polyline.Vertices.Count]; 165 | for (int i = 0; i < vertices.Length; i++) 166 | { 167 | vertices[i] = ConvertPoint(polyline.Vertices[i].Location); 168 | } 169 | 170 | shape = new PolyShape(vertices[0]); 171 | PolyShape shapePolyline = shape as PolyShape; 172 | 173 | for (int i = 1; i < vertices.Length; i++) 174 | { 175 | float bulge = (float)polyline.Vertices[i - 1].Bulge; 176 | shapePolyline.ArcToDXF(vertices[i], bulge); 177 | } 178 | 179 | if (polyline.IsClosed) 180 | { 181 | float bulge = (float)polyline.Vertices[vertices.Length - 1].Bulge; 182 | shapePolyline.ArcToDXF(vertices[0], bulge); 183 | shape.Closed = true; 184 | } 185 | } 186 | break; 187 | 188 | case DxfEntityType.LwPolyline: 189 | { 190 | DxfLwPolyline lwPolyline = entity as DxfLwPolyline; 191 | Vector2[] vertices = new Vector2[lwPolyline.Vertices.Count]; 192 | for (int i = 0; i < vertices.Length; i++) 193 | { 194 | DxfLwPolylineVertex lwpVertex = lwPolyline.Vertices[i]; 195 | vertices[i] = ConvertPoint(lwpVertex.X, lwpVertex.Y); 196 | } 197 | 198 | shape = new PolyShape(vertices[0]); 199 | PolyShape shapePolyline = shape as PolyShape; 200 | 201 | for (int i = 1; i < vertices.Length; i++) 202 | { 203 | float bulge = (float)lwPolyline.Vertices[i - 1].Bulge; 204 | shapePolyline.ArcToDXF(vertices[i], bulge); 205 | } 206 | 207 | if (lwPolyline.IsClosed) 208 | { 209 | float bulge = (float)lwPolyline.Vertices[vertices.Length - 1].Bulge; 210 | shapePolyline.ArcToDXF(vertices[0], bulge); 211 | shape.Closed = true; 212 | } 213 | } 214 | break; 215 | 216 | default: 217 | Debug.Log("Unhandled entity of type: " + entity.EntityType); 218 | break; 219 | } 220 | 221 | if (shape != null) 222 | { 223 | if (entity.IsVisible) 224 | { 225 | Color32 shapeColor = ConvertColor(entity.Color); 226 | //layerColors.TryGetValue(entity.Layer, out shapeColor); 227 | 228 | shape.colorOutline = shapeColor; 229 | shapes.Add(shape); 230 | } 231 | } 232 | } 233 | 234 | return shapes; 235 | } 236 | } 237 | 238 | #endif 239 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShapeFIlesDXF.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8007be60e4f86431e8953236ce701683 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShapeFilesSVG.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Xml; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using Unity.VectorGraphics; 6 | 7 | public class VectorShapeFilesSVG 8 | { 9 | /// 10 | /// Measurement units for SVG files. 11 | /// 12 | public enum Unit 13 | { 14 | None, 15 | Pixels, 16 | Millimeters, 17 | Centimeters, 18 | Inches 19 | } 20 | private const float InchToMM = 25.4f; 21 | private const float InchToCM = 2.54f; 22 | 23 | /// 24 | /// Convert between SVG units. 25 | /// 26 | public static string UnitSuffix(Unit unit) 27 | { 28 | switch (unit) 29 | { 30 | case Unit.Pixels: 31 | return "px"; 32 | case Unit.Millimeters: 33 | return "mm"; 34 | case Unit.Centimeters: 35 | return "cm"; 36 | case Unit.Inches: 37 | return "in"; 38 | default: 39 | return ""; 40 | } 41 | } 42 | 43 | 44 | /// 45 | /// Convert between SVG units. 46 | /// 47 | public static float ConvertUnits(float measurement, Unit fromUnit, Unit toUnit) 48 | { 49 | float result = measurement; 50 | 51 | switch (fromUnit) 52 | { 53 | case Unit.Pixels: 54 | switch (toUnit) 55 | { 56 | case Unit.Millimeters: 57 | result = measurement / Screen.dpi * InchToMM; 58 | break; 59 | case Unit.Centimeters: 60 | result = measurement / Screen.dpi * InchToCM; 61 | break; 62 | case Unit.Inches: 63 | result = measurement / Screen.dpi; 64 | break; 65 | } 66 | break; 67 | 68 | case Unit.Millimeters: 69 | switch (toUnit) 70 | { 71 | case Unit.Pixels: 72 | result = measurement * Screen.dpi / InchToMM; 73 | break; 74 | case Unit.Centimeters: 75 | result = measurement / 10f; 76 | break; 77 | case Unit.Inches: 78 | result = measurement / InchToMM; 79 | break; 80 | } 81 | break; 82 | 83 | case Unit.Centimeters: 84 | switch (toUnit) 85 | { 86 | case Unit.Pixels: 87 | result = measurement * Screen.dpi / InchToCM; 88 | break; 89 | case Unit.Millimeters: 90 | result = measurement * 10f; 91 | break; 92 | case Unit.Inches: 93 | result = measurement / InchToCM; 94 | break; 95 | } 96 | break; 97 | 98 | case Unit.Inches: 99 | switch (toUnit) 100 | { 101 | case Unit.Pixels: 102 | result = measurement * Screen.dpi; 103 | break; 104 | case Unit.Millimeters: 105 | result = measurement * InchToMM; 106 | break; 107 | case Unit.Centimeters: 108 | result = measurement * InchToCM; 109 | break; 110 | } 111 | break; 112 | } 113 | 114 | return result; 115 | } 116 | 117 | /// 118 | /// Convert Unity color to SVG string 119 | /// 120 | public static string ConvertColor(Color color) 121 | { 122 | if (color.a <= Mathf.Epsilon) return "none"; 123 | 124 | return "#" + ColorUtility.ToHtmlStringRGB(color); 125 | } 126 | 127 | private static VectorShape ParseContour(BezierContour contour, Matrix2D transform) 128 | { 129 | VectorShape vectorShape = PolyShape.Create(contour); 130 | vectorShape.TransformBy(transform); 131 | 132 | return vectorShape; 133 | } 134 | 135 | private static VectorShape TryParseShapeToCircle(Shape shape, Matrix2D transform) 136 | { 137 | if (shape.Contours.Length > 1) return null; 138 | 139 | BezierContour contour = shape.Contours[0]; 140 | if (contour.Segments.Length < 5) return null; 141 | if (!contour.Closed) return null; 142 | 143 | BezierSegment[] segments = new BezierSegment[contour.Segments.Length - 1]; 144 | for (int i = 0; i < segments.Length; i++) 145 | { 146 | segments[i].P0 = transform.MultiplyPoint(contour.Segments[i].P0); 147 | segments[i].P1 = transform.MultiplyPoint(contour.Segments[i].P1); 148 | segments[i].P2 = transform.MultiplyPoint(contour.Segments[i].P2); 149 | segments[i].P3 = transform.MultiplyPoint(contour.Segments[(i + 1)].P0); 150 | } 151 | 152 | Rect shapeBounds = VectorUtils.Bounds(VectorUtils.BezierSegmentsToPath(segments)); 153 | Vector2 center = shapeBounds.center; 154 | float radius = (shapeBounds.width + shapeBounds.height) / 4f; 155 | float error = radius / 200f; 156 | for (int i = 0; i < segments.Length; i++) 157 | { 158 | if (Mathf.Abs(Vector2.Distance(center, segments[i].P0) - radius) > error) 159 | { 160 | return null; 161 | } 162 | 163 | Vector2 midpoint = VectorUtils.Eval(segments[i], 0.5f); 164 | if (Mathf.Abs(Vector2.Distance(center, midpoint) - radius) > error) 165 | { 166 | return null; 167 | } 168 | } 169 | 170 | CircleShape circle = CircleShape.Create(center, radius); 171 | circle.colorOutline = Color.red; 172 | return circle; 173 | } 174 | 175 | private static VectorShape ParseShape(Shape shape, Matrix2D transform) 176 | { 177 | VectorShape vectorShape = PolyShape.Create(shape, transform); 178 | 179 | return vectorShape; 180 | } 181 | 182 | /// 183 | /// Add node and children into shape list. 184 | /// 185 | public static void RecurseSVGNodes(SceneNode node, Matrix2D nodeTransform, List shapes) 186 | { 187 | if (node.Shapes != null) 188 | { 189 | foreach (Shape shape in node.Shapes) 190 | { 191 | VectorShape vectorShape = TryParseShapeToCircle(shape, nodeTransform); 192 | if (vectorShape == null) 193 | { 194 | vectorShape = ParseShape(shape, nodeTransform); 195 | } 196 | if (vectorShape != null) 197 | { 198 | shapes.Add(vectorShape); 199 | } 200 | //foreach (BezierContour contour in shape.Contours) 201 | //{ 202 | // VectorShape vectorShape = ParseContour(contour, nodeTransform); 203 | // if (vectorShape != null) 204 | // { 205 | // shapes.Add(vectorShape); 206 | // if (shape.PathProps.Stroke != null) 207 | // { 208 | // vectorShape.colorOutline = shape.PathProps.Stroke.Color; 209 | // if ((vectorShape.colorOutline.g > 0.99f) && (vectorShape.colorOutline.b < Mathf.Epsilon) && (vectorShape.colorOutline.r < Mathf.Epsilon)) 210 | // { 211 | // foreach (BezierPathSegment segment in contour.Segments) 212 | // { 213 | // Debug.Log(nodeTransform.MultiplyPoint(segment.P0).x); 214 | // } 215 | // } 216 | // } 217 | // } 218 | //} 219 | } 220 | } 221 | 222 | if (node.Children != null) 223 | { 224 | foreach (SceneNode child in node.Children) 225 | { 226 | Matrix2D childTransform = nodeTransform * child.Transform; 227 | RecurseSVGNodes(child, childTransform, shapes); 228 | } 229 | } 230 | } 231 | 232 | /// 233 | /// Parse SVG into VectorShape list. 234 | /// 235 | public static List ReadSVG(System.IO.TextReader svg) 236 | { 237 | List shapes = new List(); 238 | 239 | SVGParser.SceneInfo sceneInfo = SVGParser.ImportSVG(svg); 240 | //Debug.Log(sceneInfo.SceneViewport); 241 | 242 | Matrix2D rootTransform = Matrix2D.Scale(new Vector2(0.01f, -0.01f)) * sceneInfo.Scene.Root.Transform; 243 | RecurseSVGNodes(sceneInfo.Scene.Root, rootTransform, shapes); 244 | 245 | return shapes; 246 | } 247 | 248 | protected XmlWriter svgWriter; 249 | protected Unit svgUnits; 250 | 251 | /// 252 | /// . 253 | /// 254 | public VectorShapeFilesSVG() 255 | { 256 | } 257 | 258 | /// 259 | /// Initialize an XmlWriter for outputting svg data to a stream. 260 | /// 261 | /// Output stream 262 | /// Document rect 263 | /// Unit for measurements 264 | public void Open(Stream stream, Rect bounds, Unit unit = Unit.Millimeters) 265 | { 266 | XmlWriterSettings settings = new XmlWriterSettings(); 267 | settings.Indent = true; 268 | settings.NewLineOnAttributes = false; 269 | 270 | svgWriter = XmlWriter.Create(stream, settings); 271 | svgUnits = unit; 272 | 273 | svgWriter.WriteStartDocument(); 274 | svgWriter.WriteStartElement("svg", "http://www.w3.org/2000/svg"); 275 | 276 | svgWriter.WriteStartAttribute("width"); 277 | svgWriter.WriteValue(bounds.width); 278 | svgWriter.WriteValue(UnitSuffix(unit)); 279 | svgWriter.WriteEndAttribute(); 280 | 281 | svgWriter.WriteStartAttribute("height"); 282 | svgWriter.WriteValue(bounds.height); 283 | svgWriter.WriteValue(UnitSuffix(unit)); 284 | svgWriter.WriteEndAttribute(); 285 | 286 | svgWriter.WriteStartAttribute("viewBox"); 287 | svgWriter.WriteValue(bounds.x); 288 | svgWriter.WriteValue(" "); 289 | svgWriter.WriteValue(-(bounds.y + bounds.height)); 290 | svgWriter.WriteValue(" "); 291 | svgWriter.WriteValue(bounds.width); 292 | svgWriter.WriteValue(" "); 293 | svgWriter.WriteValue(bounds.height); 294 | svgWriter.WriteEndAttribute(); 295 | } 296 | 297 | /// 298 | /// Open a new element tag in the xml. 299 | /// 300 | /// ID of element block to open 301 | public void OpenElement(string element) 302 | { 303 | svgWriter.WriteStartElement(element); 304 | } 305 | 306 | /// 307 | /// Close an element tag in the xml. 308 | /// 309 | /// ID of element block to close (ignored) 310 | public void CloseElement(string element = null) 311 | { 312 | svgWriter.WriteEndElement(); 313 | } 314 | 315 | /// 316 | /// Add a list of shapes to SVG as a new group. 317 | /// 318 | /// List of shapes 319 | /// ID of group 320 | public void AddShapeGroup(List shapes, string id) 321 | { 322 | svgWriter.WriteStartElement("g"); 323 | 324 | svgWriter.WriteStartAttribute("id"); 325 | svgWriter.WriteValue(id); 326 | svgWriter.WriteEndAttribute(); 327 | 328 | foreach (VectorShape shape in shapes) 329 | { 330 | shape.WriteToXML(svgWriter, Vector2.zero, 1f); 331 | } 332 | 333 | svgWriter.WriteEndElement(); 334 | } 335 | 336 | /// 337 | /// Add an element that reuses a defined object. 338 | /// 339 | public void AddUseElement(string id, Vector2 position, float rotation) 340 | { 341 | svgWriter.WriteStartElement("use"); 342 | 343 | svgWriter.WriteStartAttribute("xlink", "href", "http://www.w3.org/1999/xlink"); 344 | svgWriter.WriteValue("#" + id); 345 | svgWriter.WriteEndAttribute(); 346 | 347 | svgWriter.WriteStartAttribute("transform"); 348 | svgWriter.WriteValue("translate(" + position.x + ", " + -position.y + ") rotate(" + -rotation + ")"); 349 | svgWriter.WriteEndAttribute(); 350 | 351 | svgWriter.WriteEndElement(); 352 | } 353 | 354 | /// 355 | /// Close the writer. 356 | /// 357 | public void Close() 358 | { 359 | if (svgWriter == null) return; 360 | 361 | svgWriter.WriteEndElement(); 362 | svgWriter.WriteEndDocument(); 363 | 364 | svgWriter.Close(); 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShapeFilesSVG.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 98d6cc10d01924b429e5ded41bed2183 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShapeIcons.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Unity.VectorGraphics; 5 | 6 | #if UNITY_EDITOR 7 | using UnityEditor; 8 | #endif 9 | 10 | public static class VectorShapeIcons 11 | { 12 | static Camera renderCamera; 13 | static Material renderMaterial; 14 | static Mesh renderMesh; 15 | 16 | static VectorUtils.TessellationOptions tessellationOptions; 17 | 18 | static void VerifyCamera() 19 | { 20 | if (renderCamera == null) 21 | { 22 | GameObject cameraObject = GameObject.Find("Icon Render Camera"); 23 | if (cameraObject == null) 24 | { 25 | cameraObject = new GameObject("Icon Render Camera"); 26 | } 27 | renderCamera = cameraObject.GetComponent(); 28 | if (renderCamera == null) 29 | { 30 | renderCamera = cameraObject.AddComponent(); 31 | } 32 | 33 | renderCamera.orthographic = true; 34 | renderCamera.orthographicSize = 1f; 35 | renderCamera.clearFlags = CameraClearFlags.SolidColor; 36 | renderCamera.backgroundColor = Color.clear; 37 | renderCamera.nearClipPlane = 0.1f; 38 | renderCamera.farClipPlane = 100.0f; 39 | renderCamera.depth = Camera.main.depth + 1; 40 | renderCamera.enabled = false; 41 | 42 | renderMaterial = new Material(Shader.Find("UI/Default")); 43 | 44 | tessellationOptions = new VectorUtils.TessellationOptions() 45 | { 46 | StepDistance = 0.1f, 47 | MaxCordDeviation = float.MaxValue, 48 | MaxTanAngleDeviation = Mathf.PI / 16.0f, 49 | SamplingStepSize = 0.01f 50 | }; 51 | } 52 | } 53 | 54 | #if false 55 | /// 56 | /// Create a Texture2D icon of a SVG image (editor only version). 57 | /// 58 | /// String containing svg content. 59 | /// PreviewRenderUtility to use for drawing 60 | /// 61 | /// Standard header and footer will be included if not found in svgContent 62 | /// 63 | public static Texture2D GetIcon(string svgContent, PreviewRenderUtility renderUtil) 64 | { 65 | string svg; 66 | if (svgContent.StartsWith("")) 67 | { 68 | svg = svgContent; 69 | } 70 | else 71 | { 72 | svg = svgIconHeader + svgContent; 73 | } 74 | 75 | if (!svg.EndsWith(svgIconFooter)) 76 | { 77 | svg = svg + svgIconFooter; 78 | } 79 | 80 | // Parse the SVG 81 | SVGParser.SceneInfo sceneInfo = SVGParser.ImportSVG(new System.IO.StringReader(svg)); 82 | int width = Mathf.CeilToInt(sceneInfo.SceneViewport.width); 83 | int height = Mathf.CeilToInt(sceneInfo.SceneViewport.height); 84 | if ((width > 64) || (height > 64)) Debug.LogWarning("SVG icon of unusual size!"); 85 | 86 | // Save the render state and get a temporary render texture 87 | RenderTexture activeTexture = RenderTexture.active; 88 | renderUtil.camera.targetTexture = RenderTexture.GetTemporary(width * 2, height * 2, 8, RenderTextureFormat.ARGB32); 89 | renderUtil.camera.backgroundColor = Color.clear; 90 | 91 | // Generate the mesh 92 | Mesh iconMesh = new Mesh(); 93 | List iconGeometry = VectorUtils.TessellateScene(sceneInfo.Scene, tessellationOptions); 94 | VectorUtils.FillMesh(iconMesh, iconGeometry, 1f); 95 | 96 | // Activate the render texture and draw the mesh into it 97 | RenderTexture.active = renderUtil.camera.targetTexture; 98 | float cameraSize = renderUtil.camera.orthographicSize; 99 | Vector3 cameraPosition = renderUtil.camera.transform.position; 100 | renderUtil.camera.orthographicSize = sceneInfo.SceneViewport.height / 2; 101 | renderUtil.camera.transform.position = new Vector3(sceneInfo.SceneViewport.center.x, sceneInfo.SceneViewport.center.y, -1); 102 | // HACK until FillMesh() flpYAxis is fixed 103 | renderUtil.camera.transform.Rotate(0, 0, 180f); 104 | renderUtil.DrawMesh(iconMesh, Matrix4x4.identity, renderMaterial, 0); 105 | renderUtil.camera.Render(); 106 | renderUtil.camera.transform.Rotate(0, 0, 180f); 107 | renderUtil.camera.orthographicSize = cameraSize; 108 | renderUtil.camera.transform.position = cameraPosition; 109 | Texture2D iconTexture = new Texture2D(width * 2, height * 2); 110 | iconTexture.ReadPixels(new Rect(0, 0, width * 2, height * 2), 0, 0); 111 | iconTexture.Apply(); 112 | 113 | // Restore the render state and release the temporary objects 114 | RenderTexture.active = activeTexture; 115 | RenderTexture.ReleaseTemporary(renderUtil.camera.targetTexture); 116 | UnityEngine.Object.DestroyImmediate(iconMesh); 117 | 118 | return iconTexture; 119 | } 120 | 121 | /// 122 | /// Create a Texture2D icon out of a VectorShape (editor only version). 123 | /// 124 | public static Texture2D GetIcon(VectorShape shape, PreviewRenderUtility renderUtil) 125 | { 126 | VectorUtils.TessellationOptions activeOptions = VectorShape.tessellationOptions; 127 | VectorShape.tessellationOptions = tessellationOptions; 128 | shape.Dirty = true; 129 | 130 | Rect shapeBounds = shape.ShapeBounds; 131 | int width = Mathf.CeilToInt(shapeBounds.width); 132 | int height = Mathf.CeilToInt(shapeBounds.height); 133 | 134 | // Save the render state and get a temporary render texture 135 | RenderTexture activeTexture = RenderTexture.active; 136 | renderUtil.camera.targetTexture = RenderTexture.GetTemporary(width * 2, height * 2, 8, RenderTextureFormat.ARGB32); 137 | renderUtil.camera.backgroundColor = Color.clear; 138 | 139 | // Activate the render texture and draw the shape into it 140 | RenderTexture.active = renderUtil.camera.targetTexture; 141 | float cameraSize = renderUtil.camera.orthographicSize; 142 | Vector3 cameraPosition = renderUtil.camera.transform.position; 143 | renderUtil.camera.orthographicSize = shapeBounds.height / 2; 144 | renderUtil.camera.transform.position = new Vector3(shapeBounds.center.x, shapeBounds.center.y, -1); 145 | 146 | Matrix4x4 drawMatrix = Matrix4x4.identity; 147 | renderUtil.DrawMesh(shape.ShapeMesh, drawMatrix, renderMaterial, 0); 148 | 149 | renderUtil.camera.Render(); 150 | renderUtil.camera.orthographicSize = cameraSize; 151 | renderUtil.camera.transform.position = cameraPosition; 152 | Texture2D iconTexture = new Texture2D(width * 2, height * 2); 153 | iconTexture.ReadPixels(new Rect(0, 0, width * 2, height * 2), 0, 0); 154 | iconTexture.Apply(); 155 | 156 | // Restore the render state and release the temporary render texture 157 | RenderTexture.active = activeTexture; 158 | RenderTexture.ReleaseTemporary(renderUtil.camera.targetTexture); 159 | 160 | VectorShape.tessellationOptions = activeOptions; 161 | shape.Dirty = true; 162 | 163 | return iconTexture; 164 | } 165 | #endif 166 | 167 | static void OnPostRender(Camera camera) 168 | { 169 | if ((camera == renderCamera) && (renderMesh != null)) 170 | { 171 | renderMaterial.SetPass(0); 172 | Graphics.DrawMeshNow(renderMesh, Vector3.zero, Quaternion.identity); 173 | } 174 | } 175 | 176 | /// 177 | /// Create a Texture2D icon of a SVG image. 178 | /// 179 | /// String containing svg content. 180 | /// 181 | /// Standard header and footer will be included if not found in svgContent 182 | /// 183 | public static Texture2D GetIcon(string svgContent) 184 | { 185 | string svg; 186 | if (svgContent.StartsWith("")) 187 | { 188 | svg = svgContent; 189 | } 190 | else 191 | { 192 | svg = svgIconHeader + svgContent; 193 | } 194 | 195 | if (!svg.EndsWith(svgIconFooter)) 196 | { 197 | svg = svg + svgIconFooter; 198 | } 199 | 200 | // Parse the SVG 201 | SVGParser.SceneInfo sceneInfo = SVGParser.ImportSVG(new System.IO.StringReader(svg)); 202 | int width = Mathf.CeilToInt(sceneInfo.SceneViewport.width); 203 | int height = Mathf.CeilToInt(sceneInfo.SceneViewport.height); 204 | if ((width > 64) || (height > 64)) Debug.LogWarning("SVG icon of unusual size!"); 205 | 206 | // Save the render state and get a temporary render texture 207 | VerifyCamera(); 208 | RenderTexture activeTexture = RenderTexture.active; 209 | RenderTexture tempTexture = RenderTexture.GetTemporary(width, height, 8, RenderTextureFormat.ARGB32); ; 210 | 211 | // Generate the mesh 212 | renderMesh = new Mesh(); 213 | List iconGeometry = VectorUtils.TessellateScene(sceneInfo.Scene, tessellationOptions); 214 | VectorUtils.FillMesh(renderMesh, iconGeometry, 1f); 215 | 216 | // Activate the render texture and draw the mesh into it 217 | renderCamera.orthographicSize = sceneInfo.SceneViewport.height / 2; 218 | renderCamera.transform.position = new Vector3(sceneInfo.SceneViewport.center.x, sceneInfo.SceneViewport.center.y, -1); 219 | 220 | renderCamera.targetTexture = tempTexture; 221 | 222 | Camera.onPostRender += OnPostRender; 223 | 224 | // HACK until FillMesh() flipYAxis is fixed 225 | renderCamera.transform.Rotate(0, 0, 180f); 226 | renderCamera.Render(); 227 | renderCamera.transform.Rotate(0, 0, 180f); 228 | 229 | Camera.onPostRender -= OnPostRender; 230 | 231 | RenderTexture.active = renderCamera.targetTexture; 232 | 233 | Texture2D iconTexture = new Texture2D(width, height); 234 | iconTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0); 235 | iconTexture.Apply(); 236 | 237 | // Restore the render state and release the temporary objects 238 | RenderTexture.active = activeTexture; 239 | renderCamera.targetTexture = activeTexture; 240 | RenderTexture.ReleaseTemporary(tempTexture); 241 | 242 | UnityEngine.Object.DestroyImmediate(renderMesh); 243 | renderMesh = null; 244 | 245 | return iconTexture; 246 | } 247 | 248 | /// 249 | /// Create a Texture2D icon out of a VectorShape. 250 | /// 251 | public static Texture2D GetIcon(VectorShape shape) 252 | { 253 | VectorUtils.TessellationOptions activeOptions = VectorShape.tessellationOptions; 254 | VectorShape.tessellationOptions = tessellationOptions; 255 | shape.Dirty = true; 256 | 257 | Rect shapeBounds = shape.ShapeBounds; 258 | int width = Mathf.CeilToInt(shapeBounds.width); 259 | int height = Mathf.CeilToInt(shapeBounds.height); 260 | renderMesh = shape.ShapeMesh; 261 | 262 | // Save the render state and get a temporary render texture 263 | VerifyCamera(); 264 | RenderTexture activeTexture = RenderTexture.active; 265 | RenderTexture tempTexture = RenderTexture.GetTemporary(width, height, 8, RenderTextureFormat.ARGB32); ; 266 | 267 | // Activate the render texture and draw the mesh into it 268 | renderCamera.orthographicSize = shapeBounds.height / 2; 269 | renderCamera.transform.position = new Vector3(shapeBounds.center.x, shapeBounds.center.y, -1); 270 | 271 | renderCamera.targetTexture = tempTexture; 272 | 273 | Camera.onPostRender += OnPostRender; 274 | 275 | renderCamera.Render(); 276 | 277 | Camera.onPostRender -= OnPostRender; 278 | 279 | RenderTexture.active = renderCamera.targetTexture; 280 | 281 | Texture2D iconTexture = new Texture2D(width, height); 282 | iconTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0); 283 | iconTexture.Apply(); 284 | 285 | // Restore the render state and release the temporary objects 286 | RenderTexture.active = activeTexture; 287 | renderCamera.targetTexture = activeTexture; 288 | RenderTexture.ReleaseTemporary(tempTexture); 289 | 290 | VectorShape.tessellationOptions = activeOptions; 291 | shape.Dirty = true; 292 | 293 | renderMesh = null; 294 | 295 | return iconTexture; 296 | } 297 | 298 | /// 299 | /// Create a Sprite icon of a SVG image. 300 | /// 301 | /// String containing svg content. 302 | /// 303 | /// Standard header and footer will be included if not found in svgContent 304 | /// 305 | public static Sprite GetSprite(string svgContent) 306 | { 307 | string svg; 308 | if (svgContent.StartsWith("")) 309 | { 310 | svg = svgContent; 311 | } 312 | else 313 | { 314 | svg = svgIconHeader + svgContent; 315 | } 316 | 317 | if (!svg.EndsWith(svgIconFooter)) 318 | { 319 | svg = svg + svgIconFooter; 320 | } 321 | 322 | // Parse the SVG 323 | SVGParser.SceneInfo sceneInfo = SVGParser.ImportSVG(new System.IO.StringReader(svg)); 324 | int width = Mathf.CeilToInt(sceneInfo.SceneViewport.width); 325 | int height = Mathf.CeilToInt(sceneInfo.SceneViewport.height); 326 | if ((width > 64) || (height > 64)) Debug.LogWarning("SVG icon of unusual size!"); 327 | 328 | List iconGeometry = VectorUtils.TessellateScene(sceneInfo.Scene, tessellationOptions); 329 | 330 | Sprite sprite = VectorUtils.BuildSprite(iconGeometry, 100f, VectorUtils.Alignment.Center, Vector2.zero, 128, true); 331 | 332 | return sprite; 333 | } 334 | 335 | /// 336 | /// Create a Sprite icon out of a VectorShape. 337 | /// 338 | public static Sprite GetSprite(VectorShape shape) 339 | { 340 | Rect shapeBounds = shape.ShapeBounds; 341 | int width = Mathf.CeilToInt(shapeBounds.width); 342 | int height = Mathf.CeilToInt(shapeBounds.height); 343 | List iconGeometry = shape.ShapeGeometry; 344 | 345 | Sprite sprite = VectorUtils.BuildSprite(iconGeometry, 100f, VectorUtils.Alignment.Center, Vector2.zero, 128, true); 346 | 347 | return sprite; 348 | } 349 | 350 | const string svgIconHeader = 351 | "" + 352 | "" + 353 | ""; 354 | 355 | const string svgIconFooter = 356 | ""; 357 | 358 | 359 | // Editing tools 360 | public const string iconView = 361 | ""; 362 | 363 | public const string iconBrush = 364 | ""; 365 | 366 | public const string iconMagnify = 367 | ""; 368 | 369 | public const string iconArrowAll = 370 | ""; 371 | 372 | public const string iconSelectionRect = 373 | ""; 374 | 375 | public const string iconShapes = 376 | ""; 377 | 378 | 379 | // Selection levels 380 | public const string iconSelectObject = 381 | ""; 382 | 383 | public const string iconSelectSegment = 384 | ""; 385 | 386 | public const string iconSelectVertex = 387 | ""; 388 | 389 | 390 | // Simple shapes 391 | public const string iconPoint = 392 | ""; 393 | 394 | public const string iconCircle = 395 | ""; 396 | 397 | public const string iconFilledCircle = 398 | ""; 399 | 400 | public const string iconTriangle = 401 | ""; 402 | 403 | public const string iconSquare = 404 | ""; 405 | 406 | public const string iconPentagon = 407 | ""; 408 | 409 | public const string iconHexagon = 410 | ""; 411 | 412 | public const string iconPolygon = 413 | ""; 414 | 415 | public const string iconVerticalDots = 416 | ""; 417 | } 418 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShapeIcons.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8bde8653997e740ad8353e2ec03c627f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShapeUtils.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public static class VectorShapeUtils 4 | { 5 | /// 6 | /// Union of two Rects 7 | /// 8 | /// The union. 9 | /// Rect A 10 | /// Rect B 11 | public static Rect RectUnion(Rect rectA, Rect rectB) 12 | { 13 | return Rect.MinMaxRect(Mathf.Min(rectA.xMin, rectB.xMin), 14 | Mathf.Min(rectA.yMin, rectB.yMin), 15 | Mathf.Max(rectA.xMax, rectB.xMax), 16 | Mathf.Max(rectA.yMax, rectB.yMax)); 17 | } 18 | 19 | 20 | /// 21 | /// Point on a quadratic curve between pt0 and pt2. 22 | /// 23 | /// Starting point 24 | /// Control point 25 | /// Ending point 26 | /// Distance along curve 27 | public static Vector2 EvaluateQuadraticCurve(Vector2 pt0, Vector2 pt1, Vector2 pt2, float t) 28 | { 29 | Vector2 p0 = Vector2.Lerp(pt0, pt1, t); 30 | Vector2 p1 = Vector2.Lerp(pt1, pt2, t); 31 | return Vector2.Lerp(p0, p1, t); 32 | } 33 | 34 | /// 35 | /// Point on a cubic curve between pt0 and pt3. 36 | /// 37 | /// Starting point 38 | /// Control point 39 | /// Control point 40 | /// Ending point 41 | /// Distance along curve 42 | public static Vector2 EvaluateCubicCurve(Vector2 pt0, Vector2 pt1, Vector2 pt2, Vector2 pt3, float t) 43 | { 44 | Vector2 p0 = EvaluateQuadraticCurve(pt0, pt1, pt2, t); 45 | Vector2 p1 = EvaluateQuadraticCurve(pt1, pt2, pt3, t); 46 | return Vector2.Lerp(p0, p1, t); 47 | } 48 | 49 | /// 50 | /// Closest point on a line segment to given point. 51 | /// 52 | /// Test point 53 | /// Start of line segment 54 | /// End of line segment 55 | /// Closet point on segment 56 | public static Vector2 ClosestPointOnLineSegment(Vector2 pt, Vector2 segA, Vector2 segB) 57 | { 58 | float segLength = (segB - segA).sqrMagnitude; 59 | if (segLength < Mathf.Epsilon) // Segment is actually a point 60 | return segA; 61 | 62 | float t = Vector2.Dot(pt - segA, segB - segA) / segLength; 63 | if (t < 0.0) // Beyond the 'a' end of the segment 64 | return segA; 65 | if (t > 1.0) // Beyond the 'b' end of the segment 66 | return segB; 67 | 68 | // Projection falls on the segment 69 | Vector2 projection = segA + t * (segB - segA); 70 | return projection; 71 | } 72 | 73 | /// 74 | /// Distance between a point and a line segment. 75 | /// 76 | /// Test point 77 | /// Start of line segment 78 | /// End of line segment 79 | /// Distance 80 | public static float DistancePointToLineSegment(Vector2 pt, Vector2 segA, Vector2 segB) 81 | { 82 | Vector2 closest = ClosestPointOnLineSegment(pt, segA, segB); 83 | return Vector2.Distance(pt, closest); 84 | } 85 | 86 | /// 87 | /// Number of steps when approximating Bezier curves. 88 | /// 89 | public static int bezierSteps = 12; 90 | 91 | /// 92 | /// APPROXIMATE closest point on a bezier curve segment to given point. 93 | /// 94 | /// Test point 95 | /// Start of curve 96 | /// Control point A 97 | /// Control point B 98 | /// End of curve 99 | /// Closest point on the curve (approximate) 100 | public static Vector2 ClosetPointOnBezierCurve(Vector2 pt, Vector2 curveA, Vector2 controlA, Vector2 controlB, Vector2 curveB) 101 | { 102 | Vector2 closest = curveA; 103 | float sqrDistance = (pt - curveA).sqrMagnitude; 104 | 105 | float step = 1f / bezierSteps; 106 | float t = step; 107 | for (int i = 1; i < bezierSteps; i++) 108 | { 109 | Vector2 curvePt = EvaluateCubicCurve(curveA, controlA, controlB, curveB, t); 110 | float sqrDistance2 = (pt - curvePt).sqrMagnitude; 111 | if (sqrDistance2 < sqrDistance) 112 | { 113 | sqrDistance = sqrDistance2; 114 | closest = curvePt; 115 | } 116 | 117 | t += step; 118 | } 119 | 120 | return closest; 121 | } 122 | 123 | /// 124 | /// Distance between a point and a Bezier curve 125 | /// 126 | /// Test point 127 | /// Start of curve 128 | /// Control point A 129 | /// Control point B 130 | /// End of curve 131 | /// Distance (approximate) 132 | public static float DistancePointToBezierCurve(Vector2 pt, Vector2 curveA, Vector2 controlA, Vector2 controlB, Vector2 curveB) 133 | { 134 | Vector2 closest = ClosetPointOnBezierCurve(pt, curveA, controlA, curveB, controlB); 135 | 136 | return Vector2.Distance(pt, closest); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Assets/VectorShapes/VectorShapeUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6de1439a6cf1949a087f20e22cda860f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Eli Curtz 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 | -------------------------------------------------------------------------------- /PREVIEW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecurtz/UnityVectorEditor/e858f29b975dc0eb15a20bae77e3cfdc68c188b9/PREVIEW.png -------------------------------------------------------------------------------- /Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.package-manager-ui": "2.0.7", 4 | "com.unity.modules.animation": "1.0.0", 5 | "com.unity.modules.assetbundle": "1.0.0", 6 | "com.unity.modules.audio": "1.0.0", 7 | "com.unity.modules.cloth": "1.0.0", 8 | "com.unity.modules.director": "1.0.0", 9 | "com.unity.modules.imageconversion": "1.0.0", 10 | "com.unity.modules.imgui": "1.0.0", 11 | "com.unity.modules.jsonserialize": "1.0.0", 12 | "com.unity.modules.particlesystem": "1.0.0", 13 | "com.unity.modules.physics": "1.0.0", 14 | "com.unity.modules.physics2d": "1.0.0", 15 | "com.unity.modules.screencapture": "1.0.0", 16 | "com.unity.modules.tilemap": "1.0.0", 17 | "com.unity.modules.ui": "1.0.0", 18 | "com.unity.modules.uielements": "1.0.0", 19 | "com.unity.modules.umbra": "1.0.0", 20 | "com.unity.modules.unitywebrequest": "1.0.0", 21 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0", 22 | "com.unity.modules.unitywebrequestaudio": "1.0.0", 23 | "com.unity.modules.unitywebrequesttexture": "1.0.0", 24 | "com.unity.modules.unitywebrequestwww": "1.0.0", 25 | "com.unity.vectorgraphics": "1.0.0-preview.24" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!129 &1 4 | PlayerSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 15 7 | productGUID: 2a17d8bc098194c0fbe418e33bba5894 8 | AndroidProfiler: 0 9 | AndroidFilterTouchesWhenObscured: 0 10 | AndroidEnableSustainedPerformanceMode: 0 11 | defaultScreenOrientation: 4 12 | targetDevice: 2 13 | useOnDemandResources: 0 14 | accelerometerFrequency: 60 15 | companyName: DefaultCompany 16 | productName: UnityVectorEditor 17 | defaultCursor: {fileID: 0} 18 | cursorHotspot: {x: 0, y: 0} 19 | m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} 20 | m_ShowUnitySplashScreen: 1 21 | m_ShowUnitySplashLogo: 1 22 | m_SplashScreenOverlayOpacity: 1 23 | m_SplashScreenAnimation: 1 24 | m_SplashScreenLogoStyle: 1 25 | m_SplashScreenDrawMode: 0 26 | m_SplashScreenBackgroundAnimationZoom: 1 27 | m_SplashScreenLogoAnimationZoom: 1 28 | m_SplashScreenBackgroundLandscapeAspect: 1 29 | m_SplashScreenBackgroundPortraitAspect: 1 30 | m_SplashScreenBackgroundLandscapeUvs: 31 | serializedVersion: 2 32 | x: 0 33 | y: 0 34 | width: 1 35 | height: 1 36 | m_SplashScreenBackgroundPortraitUvs: 37 | serializedVersion: 2 38 | x: 0 39 | y: 0 40 | width: 1 41 | height: 1 42 | m_SplashScreenLogos: [] 43 | m_VirtualRealitySplashScreen: {fileID: 0} 44 | m_HolographicTrackingLossScreen: {fileID: 0} 45 | defaultScreenWidth: 1024 46 | defaultScreenHeight: 768 47 | defaultScreenWidthWeb: 960 48 | defaultScreenHeightWeb: 600 49 | m_StereoRenderingPath: 0 50 | m_ActiveColorSpace: 0 51 | m_MTRendering: 1 52 | m_StackTraceTypes: 010000000100000001000000010000000100000001000000 53 | iosShowActivityIndicatorOnLoading: -1 54 | androidShowActivityIndicatorOnLoading: -1 55 | iosAppInBackgroundBehavior: 0 56 | displayResolutionDialog: 1 57 | iosAllowHTTPDownload: 1 58 | allowedAutorotateToPortrait: 1 59 | allowedAutorotateToPortraitUpsideDown: 1 60 | allowedAutorotateToLandscapeRight: 1 61 | allowedAutorotateToLandscapeLeft: 1 62 | useOSAutorotation: 1 63 | use32BitDisplayBuffer: 1 64 | preserveFramebufferAlpha: 0 65 | disableDepthAndStencilBuffers: 0 66 | androidStartInFullscreen: 1 67 | androidRenderOutsideSafeArea: 0 68 | androidBlitType: 0 69 | defaultIsNativeResolution: 1 70 | macRetinaSupport: 1 71 | runInBackground: 1 72 | captureSingleScreen: 0 73 | muteOtherAudioSources: 0 74 | Prepare IOS For Recording: 0 75 | Force IOS Speakers When Recording: 0 76 | deferSystemGesturesMode: 0 77 | hideHomeButton: 0 78 | submitAnalytics: 1 79 | usePlayerLog: 1 80 | bakeCollisionMeshes: 0 81 | forceSingleInstance: 0 82 | resizableWindow: 0 83 | useMacAppStoreValidation: 0 84 | macAppStoreCategory: public.app-category.games 85 | gpuSkinning: 1 86 | graphicsJobs: 0 87 | xboxPIXTextureCapture: 0 88 | xboxEnableAvatar: 0 89 | xboxEnableKinect: 0 90 | xboxEnableKinectAutoTracking: 0 91 | xboxEnableFitness: 0 92 | visibleInBackground: 1 93 | allowFullscreenSwitch: 1 94 | graphicsJobMode: 0 95 | fullscreenMode: 1 96 | xboxSpeechDB: 0 97 | xboxEnableHeadOrientation: 0 98 | xboxEnableGuest: 0 99 | xboxEnablePIXSampling: 0 100 | metalFramebufferOnly: 0 101 | xboxOneResolution: 0 102 | xboxOneSResolution: 0 103 | xboxOneXResolution: 3 104 | xboxOneMonoLoggingLevel: 0 105 | xboxOneLoggingLevel: 1 106 | xboxOneDisableEsram: 0 107 | xboxOnePresentImmediateThreshold: 0 108 | switchQueueCommandMemory: 0 109 | switchQueueControlMemory: 16384 110 | switchQueueComputeMemory: 262144 111 | switchNVNShaderPoolsGranularity: 33554432 112 | switchNVNDefaultPoolsGranularity: 16777216 113 | switchNVNOtherPoolsGranularity: 16777216 114 | vulkanEnableSetSRGBWrite: 0 115 | m_SupportedAspectRatios: 116 | 4:3: 1 117 | 5:4: 1 118 | 16:10: 1 119 | 16:9: 1 120 | Others: 1 121 | bundleVersion: 0.1 122 | preloadedAssets: [] 123 | metroInputSource: 0 124 | wsaTransparentSwapchain: 0 125 | m_HolographicPauseOnTrackingLoss: 1 126 | xboxOneDisableKinectGpuReservation: 1 127 | xboxOneEnable7thCore: 1 128 | isWsaHolographicRemotingEnabled: 0 129 | vrSettings: 130 | cardboard: 131 | depthFormat: 0 132 | enableTransitionView: 0 133 | daydream: 134 | depthFormat: 0 135 | useSustainedPerformanceMode: 0 136 | enableVideoLayer: 0 137 | useProtectedVideoMemory: 0 138 | minimumSupportedHeadTracking: 0 139 | maximumSupportedHeadTracking: 1 140 | hololens: 141 | depthFormat: 1 142 | depthBufferSharingEnabled: 1 143 | oculus: 144 | sharedDepthBuffer: 1 145 | dashSupport: 1 146 | enable360StereoCapture: 0 147 | protectGraphicsMemory: 0 148 | enableFrameTimingStats: 0 149 | useHDRDisplay: 0 150 | m_ColorGamuts: 00000000 151 | targetPixelDensity: 30 152 | resolutionScalingMode: 0 153 | androidSupportedAspectRatio: 1 154 | androidMaxAspectRatio: 2.1 155 | applicationIdentifier: {} 156 | buildNumber: {} 157 | AndroidBundleVersionCode: 1 158 | AndroidMinSdkVersion: 16 159 | AndroidTargetSdkVersion: 0 160 | AndroidPreferredInstallLocation: 1 161 | aotOptions: 162 | stripEngineCode: 1 163 | iPhoneStrippingLevel: 0 164 | iPhoneScriptCallOptimization: 0 165 | ForceInternetPermission: 0 166 | ForceSDCardPermission: 0 167 | CreateWallpaper: 0 168 | APKExpansionFiles: 0 169 | keepLoadedShadersAlive: 0 170 | StripUnusedMeshComponents: 1 171 | VertexChannelCompressionMask: 4054 172 | iPhoneSdkVersion: 988 173 | iOSTargetOSVersionString: 9.0 174 | tvOSSdkVersion: 0 175 | tvOSRequireExtendedGameController: 0 176 | tvOSTargetOSVersionString: 9.0 177 | uIPrerenderedIcon: 0 178 | uIRequiresPersistentWiFi: 0 179 | uIRequiresFullScreen: 1 180 | uIStatusBarHidden: 1 181 | uIExitOnSuspend: 0 182 | uIStatusBarStyle: 0 183 | iPhoneSplashScreen: {fileID: 0} 184 | iPhoneHighResSplashScreen: {fileID: 0} 185 | iPhoneTallHighResSplashScreen: {fileID: 0} 186 | iPhone47inSplashScreen: {fileID: 0} 187 | iPhone55inPortraitSplashScreen: {fileID: 0} 188 | iPhone55inLandscapeSplashScreen: {fileID: 0} 189 | iPhone58inPortraitSplashScreen: {fileID: 0} 190 | iPhone58inLandscapeSplashScreen: {fileID: 0} 191 | iPadPortraitSplashScreen: {fileID: 0} 192 | iPadHighResPortraitSplashScreen: {fileID: 0} 193 | iPadLandscapeSplashScreen: {fileID: 0} 194 | iPadHighResLandscapeSplashScreen: {fileID: 0} 195 | appleTVSplashScreen: {fileID: 0} 196 | appleTVSplashScreen2x: {fileID: 0} 197 | tvOSSmallIconLayers: [] 198 | tvOSSmallIconLayers2x: [] 199 | tvOSLargeIconLayers: [] 200 | tvOSLargeIconLayers2x: [] 201 | tvOSTopShelfImageLayers: [] 202 | tvOSTopShelfImageLayers2x: [] 203 | tvOSTopShelfImageWideLayers: [] 204 | tvOSTopShelfImageWideLayers2x: [] 205 | iOSLaunchScreenType: 0 206 | iOSLaunchScreenPortrait: {fileID: 0} 207 | iOSLaunchScreenLandscape: {fileID: 0} 208 | iOSLaunchScreenBackgroundColor: 209 | serializedVersion: 2 210 | rgba: 0 211 | iOSLaunchScreenFillPct: 100 212 | iOSLaunchScreenSize: 100 213 | iOSLaunchScreenCustomXibPath: 214 | iOSLaunchScreeniPadType: 0 215 | iOSLaunchScreeniPadImage: {fileID: 0} 216 | iOSLaunchScreeniPadBackgroundColor: 217 | serializedVersion: 2 218 | rgba: 0 219 | iOSLaunchScreeniPadFillPct: 100 220 | iOSLaunchScreeniPadSize: 100 221 | iOSLaunchScreeniPadCustomXibPath: 222 | iOSUseLaunchScreenStoryboard: 0 223 | iOSLaunchScreenCustomStoryboardPath: 224 | iOSDeviceRequirements: [] 225 | iOSURLSchemes: [] 226 | iOSBackgroundModes: 0 227 | iOSMetalForceHardShadows: 0 228 | metalEditorSupport: 1 229 | metalAPIValidation: 1 230 | iOSRenderExtraFrameOnPause: 0 231 | appleDeveloperTeamID: 232 | iOSManualSigningProvisioningProfileID: 233 | tvOSManualSigningProvisioningProfileID: 234 | iOSManualSigningProvisioningProfileType: 0 235 | tvOSManualSigningProvisioningProfileType: 0 236 | appleEnableAutomaticSigning: 0 237 | iOSRequireARKit: 0 238 | iOSAutomaticallyDetectAndAddCapabilities: 1 239 | appleEnableProMotion: 0 240 | clonedFromGUID: c0afd0d1d80e3634a9dac47e8a0426ea 241 | templatePackageId: com.unity.template.3d@1.3.0 242 | templateDefaultScene: Assets/Scenes/SampleScene.unity 243 | AndroidTargetArchitectures: 5 244 | AndroidSplashScreenScale: 0 245 | androidSplashScreen: {fileID: 0} 246 | AndroidKeystoreName: 247 | AndroidKeyaliasName: 248 | AndroidBuildApkPerCpuArchitecture: 0 249 | AndroidTVCompatibility: 1 250 | AndroidIsGame: 1 251 | AndroidEnableTango: 0 252 | androidEnableBanner: 1 253 | androidUseLowAccuracyLocation: 0 254 | m_AndroidBanners: 255 | - width: 320 256 | height: 180 257 | banner: {fileID: 0} 258 | androidGamepadSupportLevel: 0 259 | resolutionDialogBanner: {fileID: 0} 260 | m_BuildTargetIcons: [] 261 | m_BuildTargetPlatformIcons: [] 262 | m_BuildTargetBatching: 263 | - m_BuildTarget: Standalone 264 | m_StaticBatching: 1 265 | m_DynamicBatching: 0 266 | - m_BuildTarget: tvOS 267 | m_StaticBatching: 1 268 | m_DynamicBatching: 0 269 | - m_BuildTarget: Android 270 | m_StaticBatching: 1 271 | m_DynamicBatching: 0 272 | - m_BuildTarget: iPhone 273 | m_StaticBatching: 1 274 | m_DynamicBatching: 0 275 | - m_BuildTarget: WebGL 276 | m_StaticBatching: 0 277 | m_DynamicBatching: 0 278 | m_BuildTargetGraphicsAPIs: 279 | - m_BuildTarget: AndroidPlayer 280 | m_APIs: 0b00000008000000 281 | m_Automatic: 1 282 | - m_BuildTarget: iOSSupport 283 | m_APIs: 10000000 284 | m_Automatic: 1 285 | - m_BuildTarget: AppleTVSupport 286 | m_APIs: 10000000 287 | m_Automatic: 0 288 | - m_BuildTarget: WebGLSupport 289 | m_APIs: 0b000000 290 | m_Automatic: 1 291 | m_BuildTargetVRSettings: 292 | - m_BuildTarget: Standalone 293 | m_Enabled: 0 294 | m_Devices: 295 | - Oculus 296 | - OpenVR 297 | m_BuildTargetEnableVuforiaSettings: [] 298 | openGLRequireES31: 0 299 | openGLRequireES31AEP: 0 300 | m_TemplateCustomTags: {} 301 | mobileMTRendering: 302 | Android: 1 303 | iPhone: 1 304 | tvOS: 1 305 | m_BuildTargetGroupLightmapEncodingQuality: [] 306 | m_BuildTargetGroupLightmapSettings: [] 307 | playModeTestRunnerEnabled: 0 308 | runPlayModeTestAsEditModeTest: 0 309 | actionOnDotNetUnhandledException: 1 310 | enableInternalProfiler: 0 311 | logObjCUncaughtExceptions: 1 312 | enableCrashReportAPI: 0 313 | cameraUsageDescription: 314 | locationUsageDescription: 315 | microphoneUsageDescription: 316 | switchNetLibKey: 317 | switchSocketMemoryPoolSize: 6144 318 | switchSocketAllocatorPoolSize: 128 319 | switchSocketConcurrencyLimit: 14 320 | switchScreenResolutionBehavior: 2 321 | switchUseCPUProfiler: 0 322 | switchApplicationID: 0x01004b9000490000 323 | switchNSODependencies: 324 | switchTitleNames_0: 325 | switchTitleNames_1: 326 | switchTitleNames_2: 327 | switchTitleNames_3: 328 | switchTitleNames_4: 329 | switchTitleNames_5: 330 | switchTitleNames_6: 331 | switchTitleNames_7: 332 | switchTitleNames_8: 333 | switchTitleNames_9: 334 | switchTitleNames_10: 335 | switchTitleNames_11: 336 | switchTitleNames_12: 337 | switchTitleNames_13: 338 | switchTitleNames_14: 339 | switchPublisherNames_0: 340 | switchPublisherNames_1: 341 | switchPublisherNames_2: 342 | switchPublisherNames_3: 343 | switchPublisherNames_4: 344 | switchPublisherNames_5: 345 | switchPublisherNames_6: 346 | switchPublisherNames_7: 347 | switchPublisherNames_8: 348 | switchPublisherNames_9: 349 | switchPublisherNames_10: 350 | switchPublisherNames_11: 351 | switchPublisherNames_12: 352 | switchPublisherNames_13: 353 | switchPublisherNames_14: 354 | switchIcons_0: {fileID: 0} 355 | switchIcons_1: {fileID: 0} 356 | switchIcons_2: {fileID: 0} 357 | switchIcons_3: {fileID: 0} 358 | switchIcons_4: {fileID: 0} 359 | switchIcons_5: {fileID: 0} 360 | switchIcons_6: {fileID: 0} 361 | switchIcons_7: {fileID: 0} 362 | switchIcons_8: {fileID: 0} 363 | switchIcons_9: {fileID: 0} 364 | switchIcons_10: {fileID: 0} 365 | switchIcons_11: {fileID: 0} 366 | switchIcons_12: {fileID: 0} 367 | switchIcons_13: {fileID: 0} 368 | switchIcons_14: {fileID: 0} 369 | switchSmallIcons_0: {fileID: 0} 370 | switchSmallIcons_1: {fileID: 0} 371 | switchSmallIcons_2: {fileID: 0} 372 | switchSmallIcons_3: {fileID: 0} 373 | switchSmallIcons_4: {fileID: 0} 374 | switchSmallIcons_5: {fileID: 0} 375 | switchSmallIcons_6: {fileID: 0} 376 | switchSmallIcons_7: {fileID: 0} 377 | switchSmallIcons_8: {fileID: 0} 378 | switchSmallIcons_9: {fileID: 0} 379 | switchSmallIcons_10: {fileID: 0} 380 | switchSmallIcons_11: {fileID: 0} 381 | switchSmallIcons_12: {fileID: 0} 382 | switchSmallIcons_13: {fileID: 0} 383 | switchSmallIcons_14: {fileID: 0} 384 | switchManualHTML: 385 | switchAccessibleURLs: 386 | switchLegalInformation: 387 | switchMainThreadStackSize: 1048576 388 | switchPresenceGroupId: 389 | switchLogoHandling: 0 390 | switchReleaseVersion: 0 391 | switchDisplayVersion: 1.0.0 392 | switchStartupUserAccount: 0 393 | switchTouchScreenUsage: 0 394 | switchSupportedLanguagesMask: 0 395 | switchLogoType: 0 396 | switchApplicationErrorCodeCategory: 397 | switchUserAccountSaveDataSize: 0 398 | switchUserAccountSaveDataJournalSize: 0 399 | switchApplicationAttribute: 0 400 | switchCardSpecSize: -1 401 | switchCardSpecClock: -1 402 | switchRatingsMask: 0 403 | switchRatingsInt_0: 0 404 | switchRatingsInt_1: 0 405 | switchRatingsInt_2: 0 406 | switchRatingsInt_3: 0 407 | switchRatingsInt_4: 0 408 | switchRatingsInt_5: 0 409 | switchRatingsInt_6: 0 410 | switchRatingsInt_7: 0 411 | switchRatingsInt_8: 0 412 | switchRatingsInt_9: 0 413 | switchRatingsInt_10: 0 414 | switchRatingsInt_11: 0 415 | switchLocalCommunicationIds_0: 416 | switchLocalCommunicationIds_1: 417 | switchLocalCommunicationIds_2: 418 | switchLocalCommunicationIds_3: 419 | switchLocalCommunicationIds_4: 420 | switchLocalCommunicationIds_5: 421 | switchLocalCommunicationIds_6: 422 | switchLocalCommunicationIds_7: 423 | switchParentalControl: 0 424 | switchAllowsScreenshot: 1 425 | switchAllowsVideoCapturing: 1 426 | switchAllowsRuntimeAddOnContentInstall: 0 427 | switchDataLossConfirmation: 0 428 | switchUserAccountLockEnabled: 0 429 | switchSystemResourceMemory: 16777216 430 | switchSupportedNpadStyles: 3 431 | switchNativeFsCacheSize: 32 432 | switchIsHoldTypeHorizontal: 0 433 | switchSupportedNpadCount: 8 434 | switchSocketConfigEnabled: 0 435 | switchTcpInitialSendBufferSize: 32 436 | switchTcpInitialReceiveBufferSize: 64 437 | switchTcpAutoSendBufferSizeMax: 256 438 | switchTcpAutoReceiveBufferSizeMax: 256 439 | switchUdpSendBufferSize: 9 440 | switchUdpReceiveBufferSize: 42 441 | switchSocketBufferEfficiency: 4 442 | switchSocketInitializeEnabled: 1 443 | switchNetworkInterfaceManagerInitializeEnabled: 1 444 | switchPlayerConnectionEnabled: 1 445 | ps4NPAgeRating: 12 446 | ps4NPTitleSecret: 447 | ps4NPTrophyPackPath: 448 | ps4ParentalLevel: 11 449 | ps4ContentID: ED1633-NPXX51362_00-0000000000000000 450 | ps4Category: 0 451 | ps4MasterVersion: 01.00 452 | ps4AppVersion: 01.00 453 | ps4AppType: 0 454 | ps4ParamSfxPath: 455 | ps4VideoOutPixelFormat: 0 456 | ps4VideoOutInitialWidth: 1920 457 | ps4VideoOutBaseModeInitialWidth: 1920 458 | ps4VideoOutReprojectionRate: 60 459 | ps4PronunciationXMLPath: 460 | ps4PronunciationSIGPath: 461 | ps4BackgroundImagePath: 462 | ps4StartupImagePath: 463 | ps4StartupImagesFolder: 464 | ps4IconImagesFolder: 465 | ps4SaveDataImagePath: 466 | ps4SdkOverride: 467 | ps4BGMPath: 468 | ps4ShareFilePath: 469 | ps4ShareOverlayImagePath: 470 | ps4PrivacyGuardImagePath: 471 | ps4NPtitleDatPath: 472 | ps4RemotePlayKeyAssignment: -1 473 | ps4RemotePlayKeyMappingDir: 474 | ps4PlayTogetherPlayerCount: 0 475 | ps4EnterButtonAssignment: 1 476 | ps4ApplicationParam1: 0 477 | ps4ApplicationParam2: 0 478 | ps4ApplicationParam3: 0 479 | ps4ApplicationParam4: 0 480 | ps4DownloadDataSize: 0 481 | ps4GarlicHeapSize: 2048 482 | ps4ProGarlicHeapSize: 2560 483 | ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ 484 | ps4pnSessions: 1 485 | ps4pnPresence: 1 486 | ps4pnFriends: 1 487 | ps4pnGameCustomData: 1 488 | playerPrefsSupport: 0 489 | enableApplicationExit: 0 490 | resetTempFolder: 1 491 | restrictedAudioUsageRights: 0 492 | ps4UseResolutionFallback: 0 493 | ps4ReprojectionSupport: 0 494 | ps4UseAudio3dBackend: 0 495 | ps4SocialScreenEnabled: 0 496 | ps4ScriptOptimizationLevel: 0 497 | ps4Audio3dVirtualSpeakerCount: 14 498 | ps4attribCpuUsage: 0 499 | ps4PatchPkgPath: 500 | ps4PatchLatestPkgPath: 501 | ps4PatchChangeinfoPath: 502 | ps4PatchDayOne: 0 503 | ps4attribUserManagement: 0 504 | ps4attribMoveSupport: 0 505 | ps4attrib3DSupport: 0 506 | ps4attribShareSupport: 0 507 | ps4attribExclusiveVR: 0 508 | ps4disableAutoHideSplash: 0 509 | ps4videoRecordingFeaturesUsed: 0 510 | ps4contentSearchFeaturesUsed: 0 511 | ps4attribEyeToEyeDistanceSettingVR: 0 512 | ps4IncludedModules: [] 513 | monoEnv: 514 | splashScreenBackgroundSourceLandscape: {fileID: 0} 515 | splashScreenBackgroundSourcePortrait: {fileID: 0} 516 | spritePackerPolicy: 517 | webGLMemorySize: 256 518 | webGLExceptionSupport: 1 519 | webGLNameFilesAsHashes: 0 520 | webGLDataCaching: 1 521 | webGLDebugSymbols: 0 522 | webGLEmscriptenArgs: 523 | webGLModulesDirectory: 524 | webGLTemplate: APPLICATION:Default 525 | webGLAnalyzeBuildSize: 0 526 | webGLUseEmbeddedResources: 0 527 | webGLCompressionFormat: 1 528 | webGLLinkerTarget: 1 529 | webGLThreadsSupport: 0 530 | scriptingDefineSymbols: {} 531 | platformArchitecture: {} 532 | scriptingBackend: {} 533 | il2cppCompilerConfiguration: {} 534 | managedStrippingLevel: {} 535 | incrementalIl2cppBuild: {} 536 | allowUnsafeCode: 0 537 | additionalIl2CppArgs: 538 | scriptingRuntimeVersion: 1 539 | apiCompatibilityLevelPerPlatform: 540 | Standalone: 3 541 | m_RenderingPath: 1 542 | m_MobileRenderingPath: 1 543 | metroPackageName: Template_3D 544 | metroPackageVersion: 545 | metroCertificatePath: 546 | metroCertificatePassword: 547 | metroCertificateSubject: 548 | metroCertificateIssuer: 549 | metroCertificateNotAfter: 0000000000000000 550 | metroApplicationDescription: Template_3D 551 | wsaImages: {} 552 | metroTileShortName: 553 | metroTileShowName: 0 554 | metroMediumTileShowName: 0 555 | metroLargeTileShowName: 0 556 | metroWideTileShowName: 0 557 | metroSupportStreamingInstall: 0 558 | metroLastRequiredScene: 0 559 | metroDefaultTileSize: 1 560 | metroTileForegroundText: 2 561 | metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} 562 | metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, 563 | a: 1} 564 | metroSplashScreenUseBackgroundColor: 0 565 | platformCapabilities: {} 566 | metroTargetDeviceFamilies: {} 567 | metroFTAName: 568 | metroFTAFileTypes: [] 569 | metroProtocolName: 570 | metroCompilationOverrides: 1 571 | XboxOneProductId: 572 | XboxOneUpdateKey: 573 | XboxOneSandboxId: 574 | XboxOneContentId: 575 | XboxOneTitleId: 576 | XboxOneSCId: 577 | XboxOneGameOsOverridePath: 578 | XboxOnePackagingOverridePath: 579 | XboxOneAppManifestOverridePath: 580 | XboxOneVersion: 1.0.0.0 581 | XboxOnePackageEncryption: 0 582 | XboxOnePackageUpdateGranularity: 2 583 | XboxOneDescription: 584 | XboxOneLanguage: 585 | - enus 586 | XboxOneCapability: [] 587 | XboxOneGameRating: {} 588 | XboxOneIsContentPackage: 0 589 | XboxOneEnableGPUVariability: 1 590 | XboxOneSockets: {} 591 | XboxOneSplashScreen: {fileID: 0} 592 | XboxOneAllowedProductIds: [] 593 | XboxOnePersistentLocalStorageSize: 0 594 | XboxOneXTitleMemory: 8 595 | xboxOneScriptCompiler: 1 596 | XboxOneOverrideIdentityName: 597 | vrEditorSettings: 598 | daydream: 599 | daydreamIconForeground: {fileID: 0} 600 | daydreamIconBackground: {fileID: 0} 601 | cloudServicesEnabled: 602 | UNet: 1 603 | luminIcon: 604 | m_Name: 605 | m_ModelFolderPath: 606 | m_PortalFolderPath: 607 | luminCert: 608 | m_CertPath: 609 | m_PrivateKeyPath: 610 | luminIsChannelApp: 0 611 | luminVersion: 612 | m_VersionCode: 1 613 | m_VersionName: 614 | facebookSdkVersion: 7.9.4 615 | facebookAppId: 616 | facebookCookies: 1 617 | facebookLogging: 1 618 | facebookStatus: 1 619 | facebookXfbml: 0 620 | facebookFrictionlessRequests: 1 621 | apiCompatibilityLevel: 6 622 | cloudProjectId: 623 | framebufferDepthMemorylessMode: 0 624 | projectName: 625 | organizationId: 626 | cloudEnabled: 0 627 | enableNativePlatformBackendsForNewInputSystem: 0 628 | disableOldInputManagerSupport: 0 629 | legacyClampBlendShapeWeights: 0 630 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Vector Editor # 2 | 3 | Simple editor window for making basic vector shapes for the experimental com.unity.vectorgraphics package. It uses some custom vector shape classes, but should be easy to modify for working directly with the Unity classes. 4 | 5 | ![Preview Image](PREVIEW.png) 6 | ## Contents ## 7 | ### License ### 8 | The Unity Vector Editor source code is released under the MIT License. 9 | ### Packages Folder ### 10 | Contains the manifest file required for importing com.unity.vectorgraphics into your Unity project. For more information check the documentation in the **[Unity Vector Graphics Samples](https://github.com/Unity-Technologies/vector-graphics-samples)**. 11 | ### Assets/VectorShapes Folder ### 12 | Contains the C# source code for some simple utility classes representing basic shapes. 13 | ### Assets/VectorShapes/Editor Folder ### 14 | Contains the C# source code for the custom editor window. 15 | 16 | -------------------------------------------------------------------------------- /UnityVectorEditor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio for Mac 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp", "Assembly-CSharp.csproj", "{BB9611A0-5675-E126-33C5-75BCF1539C1B}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor", "Assembly-CSharp-Editor.csproj", "{8EEF50EB-C261-9F49-DC6A-3A2ED6A2AD99}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {BB9611A0-5675-E126-33C5-75BCF1539C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {BB9611A0-5675-E126-33C5-75BCF1539C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {BB9611A0-5675-E126-33C5-75BCF1539C1B}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {BB9611A0-5675-E126-33C5-75BCF1539C1B}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {8EEF50EB-C261-9F49-DC6A-3A2ED6A2AD99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {8EEF50EB-C261-9F49-DC6A-3A2ED6A2AD99}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {8EEF50EB-C261-9F49-DC6A-3A2ED6A2AD99}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {8EEF50EB-C261-9F49-DC6A-3A2ED6A2AD99}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | --------------------------------------------------------------------------------