├── .gitattributes ├── .gitignore ├── DegenerateInput.xlsx ├── DegenerateLine.txt ├── Notes.txt ├── README.md ├── Screenshots ├── Dense.png ├── ExampleSchot-8-1-2015.png ├── Extra Long.png ├── HackLinePerformance.png ├── Merging_aka_Unioning.png ├── Problems.png └── Scribble.png ├── ScribbleSegmenter.sln ├── addons.make ├── emptyExample.vcxproj ├── emptyExample.vcxproj.filters ├── example ├── main.cpp ├── ofApp.cpp └── ofApp.h ├── icon.rc ├── obj ├── Debug │ └── icon.res └── Release │ └── icon.res └── src ├── FaceFinder.cpp ├── FaceFinder.h ├── HalfedgeGraph.cpp ├── HalfedgeGraph.h ├── Intersector.cpp ├── Intersector.h ├── Line.cpp ├── Line.h ├── OffsetCurves.cpp ├── OffsetCurves.h ├── PolylineGraphData.cpp ├── PolylineGraphData.h ├── PolylineGraphEmbedder.cpp ├── PolylineGraphEmbedder.h ├── PolylineGraphMain.h ├── PolylineGraphPostProcessor.cpp └── PolylineGraphPostProcessor.h /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *.db 14 | *~.nib 15 | local.properties 16 | .classpath 17 | .settings/ 18 | 19 | # Visual Studio Databases. 20 | .opendb 21 | .db 22 | .VC.VC.opendb 23 | 24 | .loadpath 25 | 26 | # External tool builders 27 | .externalToolBuilders/ 28 | 29 | # Locally stored "Eclipse launch configurations" 30 | *.launch 31 | 32 | # CDT-specific 33 | .cproject 34 | 35 | # PDT-specific 36 | .buildpath 37 | 38 | 39 | ################# 40 | ## Visual Studio 41 | ################# 42 | 43 | ## Ignore Visual Studio temporary files, build results, and 44 | ## files generated by popular Visual Studio add-ons. 45 | 46 | # User-specific files 47 | *.suo 48 | *.user 49 | *.sln.docstates 50 | 51 | # Build results 52 | 53 | [Dd]ebug/ 54 | [Rr]elease/ 55 | x64/ 56 | build/ 57 | [Bb]in/ 58 | [Oo]bj/ 59 | 60 | # MSTest test Results 61 | [Tt]est[Rr]esult*/ 62 | [Bb]uild[Ll]og.* 63 | 64 | *_i.c 65 | *_p.c 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.pch 70 | *.pdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.log 86 | *.scc 87 | 88 | # Visual C++ cache files 89 | ipch/ 90 | *.aps 91 | *.ncb 92 | *.opensdf 93 | *.sdf 94 | *.cachefile 95 | 96 | # Visual Studio profiler 97 | *.psess 98 | *.vsp 99 | *.vspx 100 | 101 | # Guidance Automation Toolkit 102 | *.gpState 103 | 104 | # ReSharper is a .NET coding add-in 105 | _ReSharper*/ 106 | *.[Rr]e[Ss]harper 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | *.ncrunch* 116 | .*crunch*.local.xml 117 | 118 | # Installshield output folder 119 | [Ee]xpress/ 120 | 121 | # DocProject is a documentation generator add-in 122 | DocProject/buildhelp/ 123 | DocProject/Help/*.HxT 124 | DocProject/Help/*.HxC 125 | DocProject/Help/*.hhc 126 | DocProject/Help/*.hhk 127 | DocProject/Help/*.hhp 128 | DocProject/Help/Html2 129 | DocProject/Help/html 130 | 131 | # Click-Once directory 132 | publish/ 133 | 134 | # Publish Web Output 135 | *.Publish.xml 136 | *.pubxml 137 | *.publishproj 138 | 139 | # NuGet Packages Directory 140 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 141 | #packages/ 142 | 143 | # Windows Azure Build Output 144 | csx 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.[Pp]ublish.xml 159 | *.pfx 160 | *.publishsettings 161 | 162 | # RIA/Silverlight projects 163 | Generated_Code/ 164 | 165 | # Backup & report files from converting an old project file to a newer 166 | # Visual Studio version. Backup files are not needed, because we have git ;-) 167 | _UpgradeReport_Files/ 168 | Backup*/ 169 | UpgradeLog*.XML 170 | UpgradeLog*.htm 171 | 172 | # SQL Server files 173 | App_Data/*.mdf 174 | App_Data/*.ldf 175 | 176 | ############# 177 | ## Windows detritus 178 | ############# 179 | 180 | # Windows image file caches 181 | Thumbs.db 182 | ehthumbs.db 183 | 184 | # Folder config file 185 | Desktop.ini 186 | 187 | # Recycle Bin used on file shares 188 | $RECYCLE.BIN/ 189 | 190 | # Mac crap 191 | .DS_Store 192 | 193 | 194 | ############# 195 | ## Python 196 | ############# 197 | 198 | *.py[cod] 199 | 200 | # Packages 201 | *.egg 202 | *.egg-info 203 | dist/ 204 | build/ 205 | eggs/ 206 | parts/ 207 | var/ 208 | sdist/ 209 | develop-eggs/ 210 | .installed.cfg 211 | 212 | # Installer logs 213 | pip-log.txt 214 | 215 | # Unit test / coverage reports 216 | .coverage 217 | .tox 218 | 219 | #Translations 220 | *.mo 221 | 222 | #Mr Developer 223 | .mr.developer.cfg 224 | -------------------------------------------------------------------------------- /DegenerateInput.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/DegenerateInput.xlsx -------------------------------------------------------------------------------- /DegenerateLine.txt: -------------------------------------------------------------------------------- 1 | points.push_back(ofPoint( 373.204 , 283.024 )); 2 | points.push_back(ofPoint( 375.055 , 284.931 )); 3 | points.push_back(ofPoint( 376.771 , 283.469 )); 4 | points.push_back(ofPoint( 377.497 , 284.512 )); 5 | points.push_back(ofPoint( 376.052 , 285.479 )); 6 | points.push_back(ofPoint( 377.149 , 287.79 )); 7 | points.push_back(ofPoint( 380.553 , 286.522 )); 8 | points.push_back(ofPoint( 380.965 , 288.513 )); 9 | points.push_back(ofPoint( 381.46 , 287.136 )); 10 | points.push_back(ofPoint( 383.057 , 288.716 )); 11 | points.push_back(ofPoint( 384.662 , 288.34 )); 12 | points.push_back(ofPoint( 384.39 , 288.904 )); 13 | points.push_back(ofPoint( 384.512 , 289.794 )); 14 | points.push_back(ofPoint( 385.122 , 288.024 )); 15 | points.push_back(ofPoint( 386.384 , 288.03 )); 16 | points.push_back(ofPoint( 388.153 , 288.03 )); 17 | points.push_back(ofPoint( 388.099 , 288.064 )); 18 | points.push_back(ofPoint( 390.439 , 289.862 )); 19 | points.push_back(ofPoint( 390.279 , 289.689 )); 20 | points.push_back(ofPoint( 392.643 , 287.648 )); 21 | points.push_back(ofPoint( 393.677 , 287.786 )); 22 | points.push_back(ofPoint( 395.676 , 285.937 )); 23 | points.push_back(ofPoint( 396.572 , 284.941 )); 24 | points.push_back(ofPoint( 398.701 , 283.783 )); 25 | points.push_back(ofPoint( 398.27 , 282.694 )); 26 | points.push_back(ofPoint( 398.148 , 282.491 )); 27 | points.push_back(ofPoint( 397.781 , 282.732 )); 28 | points.push_back(ofPoint( 396.317 , 282.827 )); 29 | points.push_back(ofPoint( 396.735 , 281.329 )); 30 | points.push_back(ofPoint( 394.129 , 281.527 )); 31 | points.push_back(ofPoint( 393.6 , 283.363 )); 32 | points.push_back(ofPoint( 392.574 , 283.89 )); 33 | points.push_back(ofPoint( 392.263 , 283.202 )); 34 | points.push_back(ofPoint( 391.433 , 281.289 )); 35 | points.push_back(ofPoint( 390.035 , 281.785 )); 36 | points.push_back(ofPoint( 389.84 , 281.404 )); 37 | points.push_back(ofPoint( 387.143 , 281.209 )); 38 | points.push_back(ofPoint( 387.224 , 282.984 )); 39 | points.push_back(ofPoint( 388.782 , 284.131 )); 40 | points.push_back(ofPoint( 388.221 , 284.971 )); 41 | points.push_back(ofPoint( 387.765 , 285.296 )); 42 | points.push_back(ofPoint( 387.095 , 287.762 )); 43 | points.push_back(ofPoint( 388.864 , 287.511 )); 44 | points.push_back(ofPoint( 387.249 , 288.469 )); 45 | points.push_back(ofPoint( 388.879 , 289.371 )); 46 | points.push_back(ofPoint( 387.38 , 291.94 )); 47 | points.push_back(ofPoint( 387.513 , 292.049 )); 48 | -------------------------------------------------------------------------------- /Notes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/Notes.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ofxScribbleSegmenter 2 | A segmentation procedure for planar graphs defined by an input polyline. 3 | 4 | ![alt text](https://github.com/Bryce-Summers/ofxScribbleSegmenter/blob/master/Screenshots/Scribble.png "Scribble") 5 | 6 | #Input: 7 | 8 | A list of 2D points defining a polyline. 9 | 10 | Alternativly you can use a list of polylines. 11 | 12 | #Output: 13 | A list of lists of points, where each list of point represents a polygon representing one face of the planar graph when the input polyline is embedded into it. 14 | 15 | #External Faces: 16 | 17 | Please note the some of the polygons in the output list will be external faces, in that they include the entire infinite cartesian plane minus the space encompassed by the union of the internal faces. 18 | 19 | Another way of saying this is that if I were to give a square-ish polyline as an input the the program, the it would produce 2 faces, one that contains the indide of the square and one that contains the outside of the square. 20 | 21 | 22 | Example Screen Shot 23 | - 24 | 25 | Please Note that the exact number may be slightly off due to randomization. 26 | 27 | ![alt text](https://github.com/Bryce-Summers/ofxScribbleSegmenter/blob/master/Screenshots/ExampleSchot-8-1-2015.png "Example and Information Output") 28 | 29 | Usage 30 | - 31 | 32 | ``` 33 | #include "ofMain.h" 34 | #include "FaceFinder.h" 35 | 36 | ... 37 | 38 | std::vector input; // The input is a polyline, *not* a polygon. 39 | 40 | 41 | 42 | FaceFinder segmenter; 43 | 44 | // Data Structures for computed information. 45 | std::vector< std::vector *> * polygonal_output; 46 | std::vector external_face_indices; 47 | 48 | // Using the FaceFinder to derive polygonal face infomation. 49 | polygonal_output = segmenter.findFaces(&input); 50 | // Appends the indices of every external face to the external_face_indices vector. 51 | segmenter.determineExternalFaces(polygonal_output, &external_face_indices); 52 | 53 | ``` 54 | 55 | It is also possible to derive faces for a list of polyline inputs. 56 | 57 | ``` 58 | std::vector *> input; 59 | 60 | 61 | 62 | // - Function is overridden by a function of the same name that takes in a 63 | // vector >; instead of a vector. 64 | polygonal_output = segmenter.findFaces(&input); 65 | 66 | ``` 67 | 68 | 69 | #Example Output (One possible run) 70 | 71 | ``` 72 | setup done! 73 | 4 Cycles! 74 | There is/are 1 external faces with 12, points in each of them respectively. 75 | 76 | Information about all of the polygons: 77 | 78 | Polygon 0 79 | --Point : 51.7093, 49.0416, 0, index = 11 80 | --Point : 51.7056, 48.465, 0, index = 4 81 | --Point : 51.7093, 49.0416, 0, index = 11 82 | --Point : 99.7357, 49.5628, 0, index = 13 83 | --Point : 99.0668, -0.946655, 0, index = 6 84 | --Point : 1.83067, 0.255829, 0, index = 10 85 | --Point : 1.7207, 100.376, 0, index = 8 86 | --Point : 52.033, 99.7853, 0, index = 12 87 | --Point : 51.7093, 49.0416, 0, index = 11 88 | --Point : 49.5549, 49.0182, 0, index = 0 89 | Polygon 1 90 | --Point : 99.7357, 49.5628, 0, index = 13 91 | --Point : 100.393, 99.2178, 0, index = 7 92 | --Point : 52.033, 99.7853, 0, index = 12 93 | --Point : 52.3484, 149.233, 0, index = 3 94 | --Point : 149.137, 149.424, 0, index = 2 95 | --Point : 150.136, 50.1098, 0, index = 1 96 | Polygon 2 97 | --Point : 149.137, 149.424, 0, index = 2 98 | --Point : 52.3484, 149.233, 0, index = 3 99 | --Point : 52.033, 99.7853, 0, index = 12 100 | --Point : 1.7207, 100.376, 0, index = 8 101 | --Point : 1.83067, 0.255829, 0, index = 10 102 | --Point : -0.580078, 0.285645, 0, index = 5 103 | --Point : 1.83067, 0.255829, 0, index = 10 104 | --Point : 1.84265, -10.6498, 0, index = 9 105 | --Point : 1.83067, 0.255829, 0, index = 10 106 | --Point : 99.0668, -0.946655, 0, index = 6 107 | --Point : 99.7357, 49.5628, 0, index = 13 108 | --Point : 150.136, 50.1098, 0, index = 1 109 | Polygon 3 110 | --Point : 99.7357, 49.5628, 0, index = 13 111 | --Point : 51.7093, 49.0416, 0, index = 11 112 | --Point : 52.033, 99.7853, 0, index = 12 113 | --Point : 100.393, 99.2178, 0, index = 7 114 | 115 | Process returned 0 (0x0) execution time : 8.427 s 116 | Press any key to continue. 117 | ``` 118 | 119 | TODO: 120 | 121 | 1. Elliminate the Comparator Errors inside of the Intersector. Evidently my comparison events are not valid comparators and through errors that can be seen when compiled in debug mode. 122 | 2. Fix comments. 123 | 3. Modernize this README and chronicle all of the features, including perhaps the offset curves. 124 | 4. Add set output types to various functions such as complemented face determination, 125 | because for many users it will make more sense to output the list of indices as a set. 126 | 5. Go and remove all of the int to size_t warnings with vector sizes to avoid errors in ridiculously large inputs. 127 | 6. FIX Union meet at 1 point only bug, because the union of all of the neighbors of a landlocked face, but not the face itself will include it because the neighborhood will be improiperly traced. 128 | 129 | -------------------------------------------------------------------------------- /Screenshots/Dense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/Screenshots/Dense.png -------------------------------------------------------------------------------- /Screenshots/ExampleSchot-8-1-2015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/Screenshots/ExampleSchot-8-1-2015.png -------------------------------------------------------------------------------- /Screenshots/Extra Long.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/Screenshots/Extra Long.png -------------------------------------------------------------------------------- /Screenshots/HackLinePerformance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/Screenshots/HackLinePerformance.png -------------------------------------------------------------------------------- /Screenshots/Merging_aka_Unioning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/Screenshots/Merging_aka_Unioning.png -------------------------------------------------------------------------------- /Screenshots/Problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/Screenshots/Problems.png -------------------------------------------------------------------------------- /Screenshots/Scribble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/Screenshots/Scribble.png -------------------------------------------------------------------------------- /ScribbleSegmenter.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 14 3 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "emptyExample", "emptyExample.vcxproj", "{7FD42DF7-442E-479A-BA76-D0022F99702A}" 4 | EndProject 5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openframeworksLib", "..\..\..\libs\openFrameworksCompiled\project\vs\openframeworksLib.vcxproj", "{5837595D-ACA9-485C-8E76-729040CE4B0B}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Win32 = Debug|Win32 10 | Debug|x64 = Debug|x64 11 | Release|Win32 = Release|Win32 12 | Release|x64 = Release|x64 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|Win32.ActiveCfg = Debug|Win32 16 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|Win32.Build.0 = Debug|Win32 17 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|x64.ActiveCfg = Debug|x64 18 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|x64.Build.0 = Debug|x64 19 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|Win32.ActiveCfg = Release|Win32 20 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|Win32.Build.0 = Release|Win32 21 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|x64.ActiveCfg = Release|x64 22 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|x64.Build.0 = Release|x64 23 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|Win32.ActiveCfg = Debug|Win32 24 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|Win32.Build.0 = Debug|Win32 25 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|x64.ActiveCfg = Debug|x64 26 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|x64.Build.0 = Debug|x64 27 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|Win32.ActiveCfg = Release|Win32 28 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|Win32.Build.0 = Release|Win32 29 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|x64.ActiveCfg = Release|x64 30 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|x64.Build.0 = Release|x64 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /addons.make: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/addons.make -------------------------------------------------------------------------------- /emptyExample.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {7FD42DF7-442E-479A-BA76-D0022F99702A} 23 | Win32Proj 24 | ScribbleSegmenter 25 | 26 | 27 | 28 | Application 29 | Unicode 30 | v140 31 | 32 | 33 | Application 34 | Unicode 35 | v140 36 | 37 | 38 | Application 39 | Unicode 40 | true 41 | v140 42 | 43 | 44 | Application 45 | Unicode 46 | true 47 | v140 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | bin\ 69 | obj\$(Configuration)\ 70 | $(ProjectName)_debug 71 | true 72 | true 73 | 74 | 75 | bin\ 76 | obj\$(Configuration)\ 77 | $(ProjectName)_debug 78 | true 79 | true 80 | 81 | 82 | bin\ 83 | obj\$(Configuration)\ 84 | false 85 | 86 | 87 | bin\ 88 | obj\$(Configuration)\ 89 | false 90 | 91 | 92 | 93 | Disabled 94 | EnableFastChecks 95 | %(PreprocessorDefinitions) 96 | MultiThreadedDebugDLL 97 | Level3 98 | %(AdditionalIncludeDirectories);src 99 | CompileAsCpp 100 | 101 | 102 | true 103 | Console 104 | false 105 | %(AdditionalDependencies) 106 | %(AdditionalLibraryDirectories) 107 | 108 | 109 | 110 | 111 | 112 | Disabled 113 | EnableFastChecks 114 | %(PreprocessorDefinitions) 115 | MultiThreadedDebugDLL 116 | Level3 117 | %(AdditionalIncludeDirectories);src 118 | CompileAsCpp 119 | true 120 | 121 | 122 | true 123 | Console 124 | false 125 | %(AdditionalDependencies) 126 | %(AdditionalLibraryDirectories) 127 | 128 | 129 | 130 | 131 | 132 | false 133 | %(PreprocessorDefinitions) 134 | MultiThreadedDLL 135 | Level3 136 | %(AdditionalIncludeDirectories);src 137 | CompileAsCpp 138 | true 139 | 140 | 141 | false 142 | false 143 | Console 144 | true 145 | true 146 | false 147 | %(AdditionalDependencies) 148 | %(AdditionalLibraryDirectories) 149 | 150 | 151 | 152 | 153 | 154 | false 155 | %(PreprocessorDefinitions) 156 | MultiThreadedDLL 157 | Level3 158 | %(AdditionalIncludeDirectories);src 159 | CompileAsCpp 160 | 161 | 162 | false 163 | false 164 | Console 165 | true 166 | true 167 | false 168 | %(AdditionalDependencies) 169 | %(AdditionalLibraryDirectories) 170 | 171 | 172 | 173 | 174 | 175 | {5837595d-aca9-485c-8e76-729040ce4b0b} 176 | 177 | 178 | 179 | 180 | /D_DEBUG %(AdditionalOptions) 181 | /D_DEBUG %(AdditionalOptions) 182 | $(OF_ROOT)\libs\openFrameworksCompiled\project\vs 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /emptyExample.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | {9a6d3ee7-09d5-4da0-96e4-6ab6e8e02d64} 9 | 10 | 11 | {25ed3758-bcaa-4a01-a0b0-e33c76bd2c5b} 12 | 13 | 14 | {b799d60c-6384-44d4-8f19-fe9e6f4d3c23} 15 | 16 | 17 | 18 | 19 | Example 20 | 21 | 22 | Example 23 | 24 | 25 | src 26 | 27 | 28 | src 29 | 30 | 31 | src 32 | 33 | 34 | src 35 | 36 | 37 | src 38 | 39 | 40 | src\PolylineGraphs 41 | 42 | 43 | src\PolylineGraphs 44 | 45 | 46 | src\PolylineGraphs 47 | 48 | 49 | 50 | 51 | Example 52 | 53 | 54 | src 55 | 56 | 57 | src 58 | 59 | 60 | src 61 | 62 | 63 | src 64 | 65 | 66 | src 67 | 68 | 69 | src\PolylineGraphs 70 | 71 | 72 | src\PolylineGraphs 73 | 74 | 75 | src\PolylineGraphs 76 | 77 | 78 | src\PolylineGraphs 79 | 80 | 81 | -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ofMain.h" 2 | #include "ofApp.h" 3 | 4 | //======================================================================== 5 | int main( ){ 6 | ofSetupOpenGL(1024,768,OF_WINDOW); // <-------- setup the GL context 7 | 8 | // this kicks off the running of my app 9 | // can be OF_WINDOW or OF_FULLSCREEN 10 | // pass in width and height too: 11 | ofRunApp(new ofApp()); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /example/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofApp.h" 2 | 3 | //-------------------------------------------------------------- 4 | 5 | void ofApp::resetState() 6 | { 7 | num = 0; 8 | merge_ID_1 = 0; 9 | merge_ID_2 = 1; 10 | display_input_polyline = false; 11 | 12 | updateMergeFaces(); 13 | } 14 | 15 | void ofApp::setup(){ 16 | 17 | bool display_input_polyline = true; 18 | 19 | merge_faces = NULL; 20 | 21 | // These initial example lists of points represent square-ish shapes. 22 | /* | 23 | * -+-----. 24 | * | | 25 | * .-----. 26 | */ 27 | 28 | // Compute an example involving 2 polylines. 29 | points.push_back(ofPoint(50, 50));//0 30 | points.push_back(ofPoint(150, 50));//0 31 | points.push_back(ofPoint(150, 150));//2 32 | points.push_back(ofPoint(52, 150));//3 33 | points.push_back(ofPoint(52, 48));//3 34 | 35 | int offset = 0; // Intersection, one external face with 12 points. 36 | //int offset = 200; // No intersection, 2 external faces with 8 points each. 37 | 38 | points_2.push_back(ofPoint(offset + 0, 0));//0 39 | points_2.push_back(ofPoint(offset + 100, 0));//0 40 | points_2.push_back(ofPoint(offset + 100, 100));//2 41 | points_2.push_back(ofPoint(offset + 2, 100));//3 42 | points_2.push_back(ofPoint(offset + 2, -10));//3 43 | 44 | std::vector< std::vector *> input; 45 | 46 | input.push_back(&points); 47 | input.push_back(&points_2); 48 | 49 | // Compute the initial graph embedding. 50 | computeEmbedding(); 51 | 52 | external_face_indices.clear(); 53 | 54 | post_processor.load_face_vector(faces); 55 | post_processor.determineComplementedFaces(&external_face_indices); 56 | 57 | 58 | cout << "setup done!" << endl; 59 | cout << faces->size() << " Cycles!" << endl; 60 | cout << "There is/are " << external_face_indices.size() << " external faces with "; 61 | 62 | int len = external_face_indices.size(); 63 | for(int i = 0; i < len; i++) 64 | { 65 | int shapes_index = external_face_indices[i]; 66 | int polygon_size = faces -> at(shapes_index) -> size(); 67 | cout << polygon_size << ", "; 68 | } 69 | 70 | cout << " points in each of them respectively." << endl; 71 | 72 | cout << "\n Information about all of the polygons: \n" << endl; 73 | 74 | len = faces -> size(); 75 | for(int i = 0; i < len; i++) 76 | { 77 | scrib::Point_Vector_Format * polygon = faces -> at(i); 78 | int polygon_len = polygon -> size(); 79 | 80 | cout << "Polygon " << i << endl; 81 | 82 | for(int j = 0; j < polygon_len; j++) 83 | { 84 | scrib::point_info p_info = polygon -> at(j); 85 | cout << " --Point : " << p_info.point << 86 | ", index = " << p_info.ID << endl; 87 | } 88 | } 89 | 90 | resetState(); 91 | 92 | } 93 | 94 | //-------------------------------------------------------------- 95 | void ofApp::update(){ 96 | 97 | } 98 | 99 | //-------------------------------------------------------------- 100 | void ofApp::draw(){ 101 | 102 | if(faces == NULL) 103 | { 104 | return; 105 | } 106 | 107 | ofSetColor(ofColor::red); 108 | ofFill(); 109 | 110 | // Draw all of the faces. 111 | // Draw the face with index 'num' as filled. 112 | int len = faces -> size(); 113 | 114 | for(int i = 0; i < len; i++) 115 | { 116 | scrib::Point_Vector_Format * points = faces->at(i); 117 | drawPath(points, 128, 1.0, i == num); 118 | } 119 | 120 | // -- Draw the entire scribble. 121 | 122 | if(display_input_polyline) 123 | { 124 | drawPath(points); 125 | } 126 | 127 | //drawPath(points_2); 128 | 129 | drawMergeFaces(merge_faces, 6); 130 | 131 | ofDrawBitmapString("Press 'A' and 'D' to cycle left and right through the faces.", 20, 170); 132 | ofDrawBitmapString("Click and drag the mouse in a wild pattern, then release to test new scribbles!", 20, 200); 133 | ofDrawBitmapString("Press 1, 2, and 3 to cycle each of 3 faces that will be unioned (merged) together.", 20, 230); 134 | 135 | } 136 | 137 | void ofApp::drawPath(scrib::Point_Vector_Format * points, int color, float strokeWidth, bool filled) 138 | { 139 | 140 | // Don't draw faces of trivial size. 141 | if (points -> size() < 1) 142 | { 143 | return; 144 | } 145 | 146 | int len2 = points->size(); 147 | 148 | ofPath p = ofPath(); 149 | p.setStrokeColor(color); 150 | p.setStrokeWidth(strokeWidth); 151 | 152 | ofPoint pt = points -> at(0).point; 153 | p.moveTo(pt.x, pt.y); 154 | 155 | for (int i2 = 1; i2 < len2; i2++) 156 | { 157 | ofPoint pt = points -> at(i2).point; 158 | p.lineTo(pt.x, pt.y); 159 | } 160 | 161 | p.setFilled(filled); 162 | p.close(); 163 | p.draw(); 164 | } 165 | 166 | void ofApp::drawPath(vector &points) 167 | { 168 | // Points 2. 169 | ofPath p3 = ofPath(); 170 | p3.setStrokeColor(128); 171 | p3.setStrokeWidth(1); 172 | 173 | int len = points.size(); 174 | 175 | if (len == 0) 176 | { 177 | return; 178 | } 179 | 180 | p3.moveTo(points[0]); 181 | for(int i = 1; i < len; i++) 182 | { 183 | p3.lineTo(points[i]); 184 | } 185 | 186 | p3.setFilled(false); 187 | p3.draw(); 188 | } 189 | 190 | //-------------------------------------------------------------- 191 | void ofApp::keyPressed(int key) { 192 | 193 | if (faces -> size() < 1) 194 | { 195 | return; 196 | } 197 | 198 | // Increment current face. 199 | if (key == 'd') 200 | { 201 | // Increment mod len. 202 | int len = faces->size(); 203 | num = (num + 1) % len; 204 | } 205 | 206 | // Decrement current face. 207 | if (key == 'a') 208 | { 209 | // Decrement mod len. 210 | int len = faces -> size(); 211 | num = (num + len - 1) % len; 212 | } 213 | 214 | // Increment face pair. 215 | 216 | // Cycle face3. 217 | if (key == '1') 218 | { 219 | int len = faces -> size(); 220 | int external_ID = external_faces.at(0); 221 | 222 | // cycles the 1st face ID to the next non complemented face. 223 | do 224 | { 225 | merge_ID_1 = (merge_ID_1 + 1) % len; // merge_ID_2; 226 | } while (merge_ID_1 == external_ID); 227 | 228 | this -> updateMergeFaces(); 229 | } 230 | 231 | // Cycle face 2. 232 | if (key == '2') 233 | { 234 | int len = faces -> size(); 235 | int external_ID = external_faces.at(0); 236 | 237 | // Cycle 2nd merge face to next uncomplemented face. 238 | do 239 | { 240 | merge_ID_2 = (merge_ID_2 + 1) % len; // merge_ID_2; 241 | } while (merge_ID_2 == external_ID); 242 | 243 | this -> updateMergeFaces(); 244 | } 245 | 246 | // Decrement face pair. 247 | if (key == '3') 248 | { 249 | int len = faces -> size(); 250 | int external_ID = external_faces.at(0); 251 | 252 | // Cycle 2nd merge face to next uncomplemented face. 253 | do 254 | { 255 | merge_ID_3 = (merge_ID_3 + 1) % len; // merge_ID_3; 256 | } while (merge_ID_3 == external_ID); 257 | 258 | this -> updateMergeFaces(); 259 | } 260 | 261 | } 262 | 263 | //-------------------------------------------------------------- 264 | void ofApp::keyReleased(int key){ 265 | 266 | } 267 | 268 | //-------------------------------------------------------------- 269 | void ofApp::mouseMoved(int x, int y ){ 270 | 271 | } 272 | 273 | //-------------------------------------------------------------- 274 | void ofApp::mouseDragged(int x, int y, int button){ 275 | 276 | last_point; 277 | 278 | ofPoint point_new = ofPoint(x, y); 279 | 280 | if(points.size() == 0 || last_point.distance(point_new) > 10.0) 281 | { 282 | points.push_back(point_new); 283 | last_point = point_new; 284 | } 285 | 286 | } 287 | 288 | //-------------------------------------------------------------- 289 | void ofApp::mousePressed(int x, int y, int button){ 290 | 291 | num = -1; 292 | points.clear(); 293 | points_2.clear(); 294 | display_input_polyline = true; 295 | } 296 | 297 | //-------------------------------------------------------------- 298 | 299 | void ofApp::mouseReleased(int x, int y, int button) 300 | { 301 | resetState(); 302 | computeEmbedding(); 303 | } 304 | 305 | void ofApp::computeEmbedding() 306 | { 307 | // Raw face_vector embedding. 308 | // Allocated and deallocated within this method. 309 | scrib::Face_Vector_Format * face_vector; 310 | 311 | switch (use_embedder) 312 | { 313 | case FACE_FINDER: 314 | face_vector = processUsingFaceFinder(); 315 | return; 316 | case POLYLINE_GRAPH_EMBEDDER: 317 | face_vector = processUsingGraphEmbedder(); 318 | } 319 | 320 | post_processor.load_face_vector(face_vector); 321 | this -> faces = post_processor.clipTails(); 322 | post_processor.load_face_vector(this -> faces); 323 | delete face_vector; 324 | 325 | 326 | cout << faces -> size() << " faces found." << endl; 327 | cout << "Number of Points = " << points.size() << endl; 328 | 329 | external_faces.clear(); 330 | post_processor.determineComplementedFaces(&external_faces); 331 | 332 | this -> updateMergeFaces(); 333 | } 334 | 335 | // Derives a face vector using the FaceFinder. 336 | scrib::Face_Vector_Format * ofApp::processUsingFaceFinder() 337 | { 338 | // The fast solver is pretty fast... 339 | scrib::Face_Vector_Format * face_vector = segmenter_fast.FindFaces(&points); 340 | 341 | cout << "Rebuilt Scribble Using Face Finder" << endl; 342 | return face_vector; 343 | } 344 | 345 | scrib::Face_Vector_Format * ofApp::processUsingGraphEmbedder() 346 | { 347 | scrib::Graph * graph = polyline_embedder.embedPolyline(&points); 348 | post_processor.freeGraph(); 349 | post_processor.load_graph(graph); 350 | scrib::Face_Vector_Format * face_vector = post_processor.convert_to_face_vectors(); 351 | 352 | cout << "Rebuilt Scribble Using PolylineGraphEmbedder" << endl; 353 | 354 | return face_vector; 355 | } 356 | 357 | //-------------------------------------------------------------- 358 | void ofApp::windowResized(int w, int h){ 359 | 360 | } 361 | 362 | //-------------------------------------------------------------- 363 | void ofApp::gotMessage(ofMessage msg){ 364 | 365 | } 366 | 367 | //-------------------------------------------------------------- 368 | void ofApp::dragEvent(ofDragInfo dragInfo){ 369 | 370 | } 371 | 372 | void ofApp::updateMergeFaces() 373 | { 374 | if (use_embedder == FACE_FINDER) 375 | { 376 | return; 377 | } 378 | 379 | cout << "Merging Faces " << merge_ID_1 << " AND " << merge_ID_2 << " AND " << merge_ID_3 << endl; 380 | 381 | // Just a normal std::set<> by an aliased name... 382 | scrib::ID_Set set; 383 | set.insert(merge_ID_1); 384 | set.insert(merge_ID_2); 385 | set.insert(merge_ID_3); 386 | 387 | if (merge_faces != NULL) 388 | { 389 | delete merge_faces; 390 | } 391 | 392 | merge_faces = post_processor.mergeFaces(&set); 393 | } 394 | 395 | void ofApp::drawMergeFaces(std::vector * faces, int thickness) 396 | { 397 | 398 | for (auto iter = faces -> begin(); iter != faces -> end(); iter++) 399 | { 400 | scrib::face_info * face = *iter; 401 | 402 | drawPath(&(face -> points), 128, thickness, false); 403 | 404 | drawMergeFaces(&(face->holes), 2); 405 | } 406 | } -------------------------------------------------------------------------------- /example/ofApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | #include "PolylineGraphMain.h" 5 | 6 | 7 | /* 8 | * Example Scribble Segmentation procedure using the PolylineGraph Embedding and Algorithm Suite. 9 | * Written by Bryce Summers on or before 8/27/2016 10 | */ 11 | 12 | class ofApp : public ofBaseApp{ 13 | 14 | public: 15 | void setup(); 16 | void update(); 17 | void draw(); 18 | 19 | void resetState(); 20 | 21 | void keyPressed(int key); 22 | void keyReleased(int key); 23 | void mouseMoved(int x, int y ); 24 | void mouseDragged(int x, int y, int button); 25 | void mousePressed(int x, int y, int button); 26 | void mouseReleased(int x, int y, int button); 27 | void windowResized(int w, int h); 28 | void dragEvent(ofDragInfo dragInfo); 29 | void gotMessage(ofMessage msg); 30 | 31 | private: 32 | 33 | vector points; 34 | vector points_2; 35 | std::vector external_face_indices; 36 | 37 | ofPoint last_point; 38 | 39 | bool display_input_polyline; 40 | 41 | scrib::Face_Vector_Format * faces; 42 | 43 | 44 | scrib::FaceFinder segmenter_fast = scrib::FaceFinder(); 45 | scrib::FaceFinder segmenter_brute = scrib::FaceFinder(false); 46 | 47 | 48 | scrib::PolylineGraphEmbedder polyline_embedder; 49 | scrib::PolylineGraphPostProcessor post_processor; 50 | 51 | 52 | #define FACE_FINDER 0 53 | #define POLYLINE_GRAPH_EMBEDDER 1 54 | 55 | const int use_embedder = POLYLINE_GRAPH_EMBEDDER; 56 | 57 | // Computes an embedding using the desired embedder. 58 | // Converts this -> points to this->shapes. 59 | // Chooses an embedder based off of the value of this->use_embedder 60 | void computeEmbedding(); 61 | scrib::Face_Vector_Format * processUsingFaceFinder(); 62 | scrib::Face_Vector_Format * processUsingGraphEmbedder(); 63 | 64 | 65 | int num; 66 | 67 | // Stuff for face merging. 68 | int merge_ID_1; 69 | int merge_ID_2; 70 | int merge_ID_3; 71 | std::vector * merge_faces; 72 | 73 | void updateMergeFaces(); 74 | void drawMergeFaces(std::vector * faces, int thickness); 75 | 76 | std::vector external_faces; 77 | 78 | // Helpful path drawing function. 79 | void drawPath(vector &points); 80 | void drawPath(scrib::Point_Vector_Format * points, int color, float strokeWidth, bool filled); 81 | }; 82 | -------------------------------------------------------------------------------- /icon.rc: -------------------------------------------------------------------------------- 1 | // Icon Resource Definition 2 | #define MAIN_ICON 102 3 | 4 | #if defined(_DEBUG) 5 | MAIN_ICON ICON "icon_debug.ico" 6 | #else 7 | MAIN_ICON ICON "icon.ico" 8 | #endif 9 | -------------------------------------------------------------------------------- /obj/Debug/icon.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/obj/Debug/icon.res -------------------------------------------------------------------------------- /obj/Release/icon.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bryce-Summers/ofxScribbleSegmenter/0154bc0fc3e6d8a6226a2e90bf371db6f7818224/obj/Release/icon.res -------------------------------------------------------------------------------- /src/FaceFinder.cpp: -------------------------------------------------------------------------------- 1 | #include "FaceFinder.h" 2 | 3 | namespace scrib { 4 | 5 | void FaceFinder::setClosed(bool isClosed) 6 | { 7 | closed_loop = isClosed; 8 | } 9 | 10 | Face_Vector_Format * FaceFinder::FindFaces(std::vector< std::vector *> * inputs) 11 | { 12 | // Make sure that the previous data is cleared. 13 | int len = inputs->size(); 14 | for (int i = 0; i < len; i++) 15 | { 16 | loadInput(inputs->at(i)); 17 | } 18 | 19 | return do_the_rest(); 20 | } 21 | 22 | Face_Vector_Format * FaceFinder::FindFaces(std::vector * inputs) 23 | { 24 | // Handle Trivial Input. 25 | if (inputs->size() <= 1) 26 | { 27 | return trivial(inputs); 28 | } 29 | 30 | // Make sure that the previous data is cleared. 31 | loadInput(inputs); 32 | 33 | return do_the_rest(); 34 | } 35 | 36 | inline Face_Vector_Format * FaceFinder::trivial(std::vector * inputs) 37 | { 38 | Face_Vector_Format * output = new Face_Vector_Format(); 39 | 40 | if (inputs->size() < 1) 41 | { 42 | return output; // Trivial empty array. 43 | } 44 | 45 | // 1 point face. 46 | Point_Vector_Format * face = new Point_Vector_Format(); 47 | 48 | // Create the one point. 49 | face -> push_back(point_info(inputs->at(0), 0)); 50 | 51 | output -> push_back(face); 52 | return output; 53 | } 54 | 55 | inline Face_Vector_Format * FaceFinder::do_the_rest() 56 | { 57 | splitIntersectionPoints(); 58 | convert_to_directedGraph(); 59 | sort_graph_by_edge_angle(); 60 | 61 | Face_Vector_Format * output = deriveFaces(); 62 | 63 | cleanup(); 64 | 65 | return output; 66 | } 67 | 68 | void FaceFinder::loadInput(std::vector * inputs) 69 | { 70 | // Populate the original points. 71 | int len = inputs->size(); 72 | 73 | // The offset is the initial index of the first input point. 74 | // We can therefore load multiple input lines and keep the indices distinct. 75 | int offset = points.size(); 76 | 77 | for (int i = 0; i < len; i++) 78 | { 79 | ofPoint input_point = inputs->at(i) + ofPoint(ofRandomf(), ofRandomf()); 80 | 81 | // A Paranoid vertical line prevention technique. 82 | if ((offset > 0 || i > 0) && points[offset + i - 1].x == input_point.x) 83 | { 84 | input_point.x += .001; 85 | } 86 | 87 | points.push_back(input_point); 88 | } 89 | 90 | // Populate the original lines. 91 | for (int i = 0; i < len - 1; i++) 92 | { 93 | lines_initial.push_back(scrib::Line(i + offset, i + offset + 1, &points)); 94 | } 95 | 96 | // Add a line connecting the first and last points on the original set of input points if 97 | // the face finder is in closed loop mode. 98 | // In other words put a duplicate copy of the initial point. 99 | if (closed_loop) 100 | { 101 | // connects last point at index (len - 1 + offset) to the first point, located at index (0 + offset). 102 | lines_initial.push_back(scrib::Line(len - 1 + offset, 0 + offset, &points)); 103 | } 104 | 105 | } 106 | 107 | void FaceFinder::splitIntersectionPoints() 108 | { 109 | 110 | scrib::Intersector intersector; 111 | 112 | // Use a custom made O(maximum vertical overlap * log(maximum vertical overlap). 113 | // Very small constant factors, cache friendly. 114 | if (bUseFastAlgo) 115 | { 116 | intersector.intersect(&lines_initial); 117 | } 118 | else 119 | { 120 | // Naive brute force algo. 121 | // N^2. Small constants. As robust as it gets. 122 | intersector.intersect_brute_force(&lines_initial); 123 | } 124 | 125 | // Populate the split sequence of lines. 126 | lines_split.clear(); 127 | 128 | int numLines = lines_initial.size(); 129 | 130 | for (int i = 0; i < numLines; i++) 131 | { 132 | scrib::Line line = lines_initial[i]; 133 | line.getSplitLines(&lines_split); 134 | } 135 | } 136 | 137 | void FaceFinder::convert_to_directedGraph() 138 | { 139 | // Initialize the map based directed graph structure. 140 | //directed_graph = new std::map *> (); 141 | //output_predicate = new std::map *>(); 142 | 143 | int numPoints = points.size(); 144 | for (int i = 0; i < numPoints; i++) 145 | { 146 | directed_graph[i] = new std::vector(); 147 | output_predicate[i] = new std::vector(); 148 | } 149 | 150 | // Add all of the lines to the directed graph structure. 151 | int numLines = lines_split.size(); 152 | for (int i = 0; i < numLines; i++) 153 | { 154 | scrib::Line * line = &(lines_split[i]); 155 | 156 | int index_a = line->p1_index; 157 | int index_b = line->p2_index; 158 | 159 | // Add both directions. 160 | addDirectedEdge(index_a, index_b); 161 | addDirectedEdge(index_b, index_a); 162 | 163 | } 164 | 165 | } 166 | 167 | // Add the directed edge from the point with index_a to the point with index_b; 168 | void FaceFinder::addDirectedEdge(int index_a, int index_b) 169 | { 170 | // Edge specification. 171 | std::vector * outgoing_indices = directed_graph[index_a]; 172 | outgoing_indices->push_back(index_b); 173 | 174 | // Output inclusion predicate initialization. 175 | std::vector * outgoing_predicates = output_predicate[index_a]; 176 | outgoing_predicates->push_back(false); 177 | } 178 | 179 | void FaceFinder::sort_graph_by_edge_angle() 180 | { 181 | // Sort each outgoing edges list. 182 | int numPoints = points.size(); 183 | for (int i = 0; i < numPoints; i++) 184 | { 185 | 186 | std::vector * outgoing_indices = directed_graph[i]; 187 | sort_vertice_by_edge_angle(i, outgoing_indices); 188 | 189 | } 190 | } 191 | 192 | void FaceFinder::sort_vertice_by_edge_angle(int center_point_index, std::vector * outgoing_indices) 193 | { 194 | // Initialize useful information. 195 | int len = outgoing_indices->size(); 196 | std::vector angles; 197 | ofPoint center = points[center_point_index]; 198 | 199 | // Populate the angles array with absolute relative angles. 200 | for (int i = 0; i < len; i++) 201 | { 202 | int point_index = outgoing_indices->at(i); 203 | ofPoint point = points[point_index]; 204 | 205 | float angle = atan2(point.y - center.y, point.x - center.x); 206 | angles.push_back(angle); 207 | } 208 | 209 | // Insertion sort based on the angles. 210 | for (int i = 1; i < len; i++) 211 | { 212 | for (int i2 = i - 1; i2 >= 0; i2--) 213 | { 214 | int i1 = i2 + 1; 215 | 216 | if (angles[i2] <= angles[i1]) 217 | { 218 | break; 219 | } 220 | 221 | 222 | // -- Swap at indices i2 and i2 + 1. 223 | // Keep the angle measurements synchronized with the 224 | float temp_f = angles.at(i2); 225 | angles[i2] = angles[i1]; 226 | angles[i1] = temp_f; 227 | 228 | int temp_i = outgoing_indices->at(i2); 229 | outgoing_indices->at(i2) = outgoing_indices->at(i1); 230 | outgoing_indices->at(i1) = temp_i; 231 | } 232 | } 233 | 234 | } 235 | 236 | Face_Vector_Format * FaceFinder::deriveFaces() 237 | { 238 | // -- Initialize Output Structures. 239 | Face_Vector_Format * output = new Face_Vector_Format(); 240 | 241 | // For all edges, output their cycle once. 242 | 243 | int numPoints = points.size(); 244 | 245 | // Iterate through all originating points. 246 | for (int point_index = 0; point_index < numPoints; point_index++) 247 | { 248 | std::vector * outgoing_vertices = directed_graph[point_index]; 249 | std::vector * outgoing_predicates = output_predicate[point_index]; 250 | 251 | int numEdges = outgoing_vertices->size(); 252 | 253 | // Iterate through all outgoing edges at each point. 254 | for (int edge_index = 0; edge_index < numEdges; edge_index++) 255 | { 256 | // Skip Edges that have already been processed. 257 | if (outgoing_predicates->at(edge_index)) 258 | { 259 | continue; 260 | } 261 | 262 | int p2 = outgoing_vertices->at(edge_index); 263 | output -> push_back(getCycle(point_index, p2, edge_index)); 264 | } 265 | } 266 | 267 | return output; 268 | } 269 | 270 | Point_Vector_Format * FaceFinder::getCycle(int p1_original, int p2_original, int p2_original_outgoing_index) 271 | { 272 | Point_Vector_Format * output = new Point_Vector_Format(); 273 | 274 | int i1 = p1_original; 275 | int i2 = p2_original; 276 | 277 | // The outgoing index is always the index such that p2 = dg[p1][outgoing_index]. 278 | int outgoing_index = p2_original_outgoing_index; 279 | 280 | // Iterate until we have come back to the beginning of the cycle. 281 | // Push points on the cycle at each point in time. 282 | do 283 | { 284 | setPredicate(i1, outgoing_index);// Mark the half edge to avoid redundant processing. 285 | output->push_back(point_info(points[i2], i2));// i2 is the Global index. 286 | getNextEdge(&i1, &i2, &outgoing_index); 287 | } while (i1 != p1_original || i2 != p2_original); 288 | 289 | return output; 290 | } 291 | 292 | void FaceFinder::getNextEdge(int * p1_index, int * p2_index, int * outgoing_index) 293 | { 294 | int p1 = *p1_index; 295 | int p2 = *p2_index; 296 | 297 | // The outgoing vertices for edges originating at p2; 298 | std::vector * p2_edges = directed_graph[p2]; 299 | 300 | // Search for the reverse edge. 301 | int index = find_outgoing_index_of_edge(p2, p1); 302 | 303 | // Compute the next edge in sorted order. 304 | int len = p2_edges->size(); 305 | 306 | int p2_outgoing_index = (index + 1) % len; 307 | int p3 = p2_edges->at(p2_outgoing_index); 308 | 309 | *p1_index = p2; 310 | *p2_index = p3; 311 | *outgoing_index = p2_outgoing_index; 312 | 313 | } 314 | 315 | void FaceFinder::setPredicate(int p1, int p2_index) 316 | { 317 | std::vector * predicates = output_predicate[p1]; 318 | predicates->at(p2_index) = true; 319 | } 320 | 321 | int FaceFinder::find_outgoing_index_of_edge(int p1, int p2) 322 | { 323 | std::vector * edges = directed_graph[p1]; 324 | 325 | int len = edges->size(); 326 | 327 | // Search for the index. 328 | for (int i = 0; i < len; i++) 329 | { 330 | if (edges->at(i) == p2) 331 | { 332 | return i; 333 | } 334 | } 335 | 336 | cout << "Error: Reverse edge not found." << endl; 337 | exit(-7); 338 | 339 | // Not found. 340 | return -1; 341 | } 342 | 343 | void FaceFinder::cleanup() 344 | { 345 | // Remove the previous data. 346 | 347 | // Erase the stack class allocated memory. 348 | points.clear(); 349 | lines_initial.clear(); 350 | lines_split.clear(); 351 | 352 | // -- Deallocate the memory heap allocated in this class. 353 | 354 | //std::map *> directed_graph 355 | 356 | for (std::map *>::iterator iter = directed_graph.begin(); iter != directed_graph.end(); ++iter) 357 | { 358 | //iter->first; 359 | delete iter->second; 360 | 361 | } 362 | 363 | directed_graph.clear(); 364 | 365 | //std::map *> output_predicate; 366 | 367 | for (std::map *>::iterator iter = output_predicate.begin(); iter != output_predicate.end(); ++iter) 368 | { 369 | //iter->first; 370 | delete iter->second; 371 | } 372 | 373 | output_predicate.clear(); 374 | 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/FaceFinder.h: -------------------------------------------------------------------------------- 1 | #ifndef FACEFINDER_H 2 | #define FACEFINDER_H 3 | 4 | /* 5 | * Transforms a set of input polylines into a planar graph embedding. 6 | * 7 | * Written by Bryce Summers. 8 | * 6/26/2015: Wrote Original. 9 | * 8/16/2016: Rewrote as PlanarGraphEmbedder in order to output full graph embeddings using a half edge structure. 10 | * This class remains as a very focused class. 11 | * 8/18/2016: Post proccessing operations have been moved to PolylineGraphEmbedder 12 | * 13 | * Written for the STUDIO for Creative Inquiry at Carnegie Mellon University. 14 | */ 15 | 16 | #include 17 | #include "ofMain.h" 18 | #include "Line.h" 19 | #include "Intersector.h" 20 | #include "PolylineGraphPostProcessor.h" // point_info definition. 21 | 22 | namespace scrib { 23 | 24 | 25 | class FaceFinder 26 | { 27 | public: 28 | 29 | // A User can explicitly pass false to force the intersection points to be found using a brute force algorithm that 30 | // may potentially be more robust and reliable than the optimized intersection algorithm, 31 | // but it kills the performance. 32 | FaceFinder(bool useFastAlgo = true) 33 | { 34 | bUseFastAlgo = useFastAlgo; 35 | closed_loop = false; 36 | }; 37 | virtual ~FaceFinder() {}; 38 | 39 | 40 | /* -- Here is the interface for calling the built in algorithms for the Scribble segmenter. 41 | * These algorithms include: 42 | * Preprocessing: 43 | * [EMPTY] 44 | * 45 | * Main Algorithm: 46 | * 1. The main algorithm for embedding a set of polylines in space and determining the set of non chordal cycles in the 47 | * associated embedded planar graph. 48 | * 49 | * - A polygon is closed if it has identical starting and ending points and open otherwise. 50 | * The algorithm may be configured to output either open or closed polygons based on the closed_loop mode state. 51 | * 52 | * FIXME: If a user draws a second line completely around an original line, then their will be faces defined by both an external 53 | * face on the original polyline embedding and an internal face on the new enclosing embedding. 54 | * This may invalidate some users' assumptions of a global planar graph embedding without any holes. 55 | * 56 | * Post Processing: 57 | * 1. Determine internal and external faces. (Initial Release) 58 | * 2. Determine trivial and non trivial area faces according to a constant area threshold value. (8/11/2016) 59 | * (If you can't think of any good constant values, you might want to look at the field of 60 | * Topological Data Analysis and their 'barcode' concept: https://en.wikipedia.org/wiki/Topological_data_analysis. 61 | * 3. Clipping off tails, i.e. portions of faces that enclose 0 area. (8/11/2016) 62 | * This could potentially be put into the getCycle function, but I think that it is best to make this a dedicated post processing step instead 63 | * in order to preserve the simplicity of the main algorithm. 64 | * This algorithm properly handles faces with either duplicated or non-duplicated starting and ending points. 65 | * i.e. Those produced in open and closed mode. 66 | */ 67 | 68 | // Derive faces from a single polyline input. 69 | // No guarantee is made about the order of the polygons. 70 | // The Output is a list of sub polygons. 71 | // All of the points given as inputs to this algorithm will be treated as if they were distinct. 72 | // The points will also be randomly offset by a 'small' amount to prevent the existence of vertical lines. 73 | /*std::vector< // List of Polygons. 74 | std::vector< // Each polygon is a list of points. 75 | point_info // Information about the point. 76 | > *> * 77 | */ 78 | Face_Vector_Format * FindFaces(std::vector * inputs); 79 | 80 | // Derive faces from a set list of vertex disjoint polyline inputs. 81 | /*std::vector< // List of Polygons. 82 | std::vector< // Each polygon is a list of points. 83 | point_info // Information about the Point. 84 | > *> * 85 | */ 86 | Face_Vector_Format * FindFaces(std::vector< std::vector *> * inputs); 87 | 88 | // Tells this face finder to interpret the input curve as a line if open and a closed loop if closed. 89 | // If close, it will consider endpoints as attached to each other. 90 | void setClosed(bool isClosed); 91 | 92 | 93 | protected: 94 | private: 95 | 96 | // The trivial function constructs the proper output for input polylines of size 1 or 0. 97 | inline Face_Vector_Format * trivial(std::vector * inputs); 98 | inline Face_Vector_Format * do_the_rest(); 99 | 100 | bool bUseFastAlgo; 101 | bool closed_loop; 102 | 103 | // Initializes the original lines from the input points. 104 | // Starts up the indexed collection of points. 105 | void loadInput(std::vector * inputs); 106 | 107 | // Intersects the original lines and splits them. 108 | void splitIntersectionPoints(); 109 | 110 | // Convert the set of lines into a directed graph. 111 | void convert_to_directedGraph(); 112 | 113 | void addDirectedEdge(int p1, int p2); 114 | 115 | // Sort the graph by the cartesian angle of the edges. 116 | void sort_graph_by_edge_angle(); 117 | 118 | // Sorts the outgoing_indices by the angles of the lines from the center 119 | // point to the points cooresponding to the outgoing indices. 120 | void sort_vertice_by_edge_angle(int center_point_index, std::vector * outgoing_indices); 121 | 122 | // Uses the computed data structures to construct the set of all cycle lists. 123 | Face_Vector_Format * deriveFaces(); 124 | 125 | /* Outputs the cycle containing the directed edge p1 --> p2, where p2 = dg[p1][p2_index]. 126 | * Traces cycles by always consistently following the rightmost edges. 127 | * (It could be leftmost and would still work as long as it is consistently left or consistently right, but not mixed...) 128 | * All edges are traced twice, once in each direction. For planar directed graphs, this is guaranteed to produce every cycle. 129 | * The "output_predicate" structure is used to keep track of which edges have been outputted. 130 | * Every edge direction is guaranteed to be in exactly one cycle. Each undirected edge can be though of as being in two cycles. 131 | */ 132 | Point_Vector_Format * getCycle(int p1, int p2, int p2_index); 133 | 134 | /* INPUT : the indices of the directed edge p1 --> p2. 135 | * 136 | * OUTPUT : the indice pointers will point to the new directed edge p1' --> p2' 137 | * NOTE : Indices are integers corresponding to the location in the global points array for each point. 138 | */ 139 | void getNextEdge(int * p1_index, int * p2_index, int * outgoing_index); 140 | 141 | // Sets the predicate associated with the given directed edge. 142 | void setPredicate(int p1, int p2); 143 | 144 | // Returns the index i, such that dg[p1][i] = p2; 145 | // Returns -1 if index not found. (This should be an error for this project.) 146 | int find_outgoing_index_of_edge(int p1, int p2); 147 | 148 | // Free all of the data structures. 149 | void cleanup(); 150 | 151 | // -- Data Structures. 152 | 153 | // The canonical collection of points at their proper indices. 154 | std::vector points; 155 | 156 | // The original input lines. 157 | std::vector lines_initial; 158 | // Split version of original input lines, where lines only intersect at vertices. 159 | std::vector lines_split; 160 | 161 | // The directed graph that represents edges between points. 162 | // Each of the integers represents an index into the points array. 163 | // The output for a Y = lookup(x) is the set of all outgoing edges x --> y \in Y. 164 | std::map *> directed_graph; 165 | 166 | // A structure that keeps track of which edges have been added to an output cycle yet. 167 | // true --> do not process this edge again, it is already in an output cycle. 168 | // false --> proccess this edge, it is part of a cycle that has not yet been output. 169 | std::map *> output_predicate; 170 | 171 | }; 172 | 173 | } 174 | 175 | #endif // FACEFINDER_H 176 | -------------------------------------------------------------------------------- /src/HalfedgeGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "HalfedgeGraph.h" 2 | 3 | namespace scrib 4 | { 5 | 6 | Face * Graph::newFace() 7 | { 8 | int ID = faces.size(); 9 | Face * output = new Face(); 10 | faces.push_back(output); 11 | output -> ID = ID; 12 | return output; 13 | } 14 | Vertex * Graph::newVertex() 15 | { 16 | int ID = vertices.size(); 17 | Vertex * output = new Vertex(); 18 | vertices.push_back(output); 19 | output -> ID = ID; 20 | return output; 21 | } 22 | 23 | Edge * Graph::newEdge() 24 | { 25 | int ID = edges.size(); 26 | Edge * output = new Edge(); 27 | edges.push_back(output); 28 | output -> ID = ID; 29 | return output; 30 | } 31 | 32 | Halfedge * Graph::newHalfedge() 33 | { 34 | int ID = halfedges.size(); 35 | Halfedge * output = new Halfedge(); 36 | halfedges.push_back(output); 37 | output -> ID = ID; 38 | return output; 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/HalfedgeGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | 5 | /* 6 | * Halfedge Graph / Mesh class. 7 | * 8 | * Written by Bryce Summers. 9 | * 10 | * 8/17/2016: Finished purely connectivist version with association. 11 | * 12 | * Usage: 13 | * 14 | * The Application programmer / New Media Artist specifies the following: 15 | * 1. The data format they have availible for graph construction (e.g. vectors of openframeworks points.) 16 | * 2. The Algorithms they need. 17 | * 3. The data format that they wish to receive the results in. 18 | * 19 | * The algorithm designer / computational geometer specifies the following: 20 | * 1. Definitions for the associated data. 21 | * 2. The interface for constructing Graphs from application data 22 | * 3. The interface for running algorithms on the Graphs. 23 | * 4. The interface for allowing the application programmer to retrieve the results. 24 | * 25 | * If done elegantly, the New Media Artist should never need to touch the halfedge mesh, go on pointer journeys, and 26 | * they should be able to treat the internal implementation as a black box. 27 | */ 28 | 29 | namespace scrib 30 | { 31 | // == Forward Declaration of classes. 32 | 33 | // -- Connectivity Elements. 34 | 35 | // Represents an entire planar graph embedding. 36 | class Graph; 37 | class Vertex; 38 | class Face; 39 | class Halfedge; 40 | class Edge; 41 | 42 | // Iterators for accessing the elements from the Graph object. 43 | // FIXME: I may wish to use lists instead of vectors if I ever wish to delete objects. 44 | // For now they are great because they allow random access to the elements by ID. 45 | typedef vector::iterator Vertex_Iter; 46 | typedef vector::iterator Edge_Iter; 47 | typedef vector::iterator Face_Iter; 48 | typedef vector::iterator Halfedge_Iter; 49 | 50 | // -- Associated Data. 51 | // The classes will be defined in application specific files so that this halfedge mesh header file may be reused. 52 | class Graph_Data; 53 | class Vertex_Data; 54 | class Face_Data; 55 | class Halfedge_Data; 56 | class Edge_Data; 57 | 58 | // FIXME: Clean up this prose. 59 | 60 | // -- Structural definition of classes. 61 | // Every class is specified by its connectivity information and a pointer to associated user data. 62 | 63 | // All elements may be marked and unmarked by algorithms and users to specific sets of elements that meet various criteria. 64 | 65 | // The Graph class represents an entire graph embedding defined by points in space. 66 | // For the purposes of the facefinder, the output graph will be planar. 67 | // connected via edges that intersect only at vertices. 68 | // The FaceFinder class may be used to derive a Graph from a set of potentially intersecting input polylines. 69 | class Graph 70 | { 71 | 72 | // Graph classes are where all of the actual data will be stored, so it contains vectors of valued data, 73 | // rather than pointers. 74 | // All ID's contained within these vectors will reference tha index of the object within these vectors. 75 | 76 | // Ideally, vertices, edges, and halfedges will be ordered logically according to the order they were input into the facefinder, 77 | // but I will need to do some more thinking on how to formally specify these things. 78 | 79 | private: 80 | 81 | // Note: We use pointers, because then the location on the heap for the elements is permanant. 82 | std::vector faces; 83 | std::vector vertices; 84 | std::vector edges; 85 | std::vector halfedges; 86 | 87 | public: 88 | 89 | // Extra Application specific information. 90 | Graph_Data * data; 91 | 92 | // -- Public Interface. 93 | 94 | // Allocation functions. 95 | Face * newFace(); 96 | Vertex * newVertex(); 97 | Edge * newEdge(); 98 | Halfedge * newHalfedge(); 99 | 100 | // Accessing functions. We keep this interface, because then we only have to guranteed that the this.get(element.ID) = element. 101 | // We could even change the internal structure to a non contiguous lookup and the interface would be preserved. 102 | 103 | Face * getFace(int ID) 104 | { 105 | return faces[ID]; 106 | } 107 | 108 | Vertex * getVertex(int ID) 109 | { 110 | return vertices[ID]; 111 | } 112 | 113 | Edge * getEdge(int ID) 114 | { 115 | return edges[ID]; 116 | } 117 | 118 | Halfedge * getHalfedge(int ID) 119 | { 120 | return halfedges[ID]; 121 | } 122 | 123 | size_t numFaces() 124 | { 125 | return faces.size(); 126 | } 127 | 128 | size_t numVertices() 129 | { 130 | return vertices.size(); 131 | } 132 | 133 | size_t numEdges() 134 | { 135 | return edges.size(); 136 | } 137 | 138 | // Should theoretically be numEdges * 2. 139 | size_t numHalfedges() 140 | { 141 | return halfedges.size(); 142 | } 143 | 144 | // -- Iteration functions. 145 | 146 | Face_Iter facesBegin() { return faces.begin(); } 147 | Face_Iter facesEnd() { return faces.end(); } 148 | 149 | Vertex_Iter verticesBegin() { return vertices.begin(); } 150 | Vertex_Iter verticesEnd() { return vertices.end(); } 151 | 152 | Edge_Iter edgesBegin() { return edges.begin(); } 153 | Edge_Iter edgesEnd() { return edges.end(); } 154 | 155 | Halfedge_Iter halfedgesBegin() { return halfedges.begin(); } 156 | Halfedge_Iter halfedgesEnd() { return halfedges.end(); } 157 | 158 | }; 159 | 160 | class Face 161 | { 162 | public: 163 | 164 | // Representative from the interior loop of halfedges defining the boundary of the face. 165 | Halfedge * halfedge; 166 | 167 | Face_Data * data; 168 | int ID; 169 | }; 170 | 171 | class Vertex 172 | { 173 | public: 174 | 175 | // A representative halfedge that is traveling away from this Vertex. 176 | // this -> halfedge -> vertex = this. 177 | Halfedge * halfedge; 178 | 179 | Vertex_Data * data; 180 | int ID; 181 | }; 182 | 183 | // Non directed edges, very useful for getting consecutive ID's within input polylines. 184 | class Edge 185 | { 186 | public: 187 | 188 | Halfedge * halfedge; 189 | 190 | Edge_Data * data; 191 | int ID; 192 | }; 193 | 194 | 195 | class Halfedge 196 | { 197 | public: 198 | 199 | Halfedge * twin; 200 | Halfedge * next; 201 | Halfedge * prev; 202 | 203 | Face * face; 204 | Edge * edge; 205 | Vertex * vertex; 206 | 207 | Halfedge_Data * data; 208 | int ID; 209 | }; 210 | } -------------------------------------------------------------------------------- /src/Intersector.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Intersector.cpp 3 | * 4 | * Written by Bryce Summers on 7/23/2015. 5 | * 6 | * Referenced: https://github.com/alexiswolfish/ofxSweepLine 7 | * 8 | */ 9 | 10 | #include 11 | #include 12 | #include "Intersector.h" 13 | 14 | namespace scrib { 15 | 16 | // Naive N^2 Intersection Algorithm. 17 | void Intersector::intersect_brute_force(std::vector * lines) 18 | { 19 | int numLines = lines->size(); 20 | for (int a = 0; a < numLines; a++) 21 | for (int b = a + 1; b < numLines; b++) 22 | { 23 | lines->at(a).intersect(&(lines->at(b))); 24 | } 25 | } 26 | 27 | void Intersector::intersect(std::vector * lines) 28 | { 29 | EventPQ event_queue(lines); 30 | TupleBST tuple_bst; 31 | 32 | int len = event_queue.size(); 33 | 34 | for (int i = 0; i < len; i++) 35 | //while(!event_queue.isEmpty()) 36 | { 37 | Event event = event_queue.delMin(); 38 | 39 | switch (event.type) 40 | { 41 | case Event::ENTER: 42 | 43 | tuple_bst.addTuple(event.tuple1); 44 | continue; 45 | 46 | case Event::EXIT: 47 | 48 | tuple_bst.removeTuple(event.tuple1); 49 | continue; 50 | } 51 | } 52 | 53 | } 54 | 55 | // ============================================================ 56 | // Event Priority Queue Methods. 57 | // ----------------------------- 58 | 59 | // -- Constructor. 60 | EventPQ::EventPQ(std::vector * lines) 61 | { 62 | int len = lines->size(); 63 | 64 | for (int i = 0; i < len; i++) 65 | { 66 | scrib::Line * line = &(lines->at(i)); 67 | 68 | Event enter; 69 | Event exit; 70 | 71 | ofPoint p1 = line->p1; 72 | ofPoint p2 = line->p2; 73 | 74 | // Enter at least x coordinate. 75 | // Exit at greatest x coordinate. 76 | // We are assuming that there are no vertical lines. 77 | if (p1.x < p2.x) 78 | { 79 | populateEvent(enter, exit, p1, p2, line); 80 | } 81 | else 82 | { 83 | populateEvent(enter, exit, p2, p1, line); 84 | } 85 | 86 | PQ.insert(enter); 87 | PQ.insert(exit); 88 | 89 | //cout << "ENTER EVENT Generated : " << enter.x << ", " << enter.y << endl; 90 | //cout << "EXIT EVENT Generated : " << exit.x << ", " << exit.y << endl; 91 | 92 | } 93 | 94 | //cout << endl; 95 | } 96 | 97 | void EventPQ::populateEvent(Event &enter, Event &exit, ofPoint &p1, ofPoint &p2, scrib::Line * line) 98 | { 99 | enter.type = Event::ENTER; 100 | exit.type = Event::EXIT; 101 | 102 | enter.x = p1.x; 103 | enter.y = p1.y; 104 | 105 | exit.x = p2.x; 106 | exit.y = p2.y; 107 | 108 | LineTuple * line_tuple = new LineTuple(); 109 | line_tuple->x = p1.x; 110 | line_tuple->y = p1.y; 111 | line_tuple->line = line; 112 | line_tuple->slope = (p2.y - p1.y) / (p2.x - p1.x); 113 | 114 | enter.tuple1 = line_tuple; 115 | exit.tuple1 = line_tuple; 116 | } 117 | 118 | Event EventPQ::delMin() 119 | { 120 | set::iterator iter = PQ.begin(); 121 | Event output = *iter; 122 | PQ.erase(iter); 123 | return output; 124 | } 125 | 126 | bool EventPQ::isEmpty() 127 | { 128 | return PQ.empty(); 129 | } 130 | 131 | 132 | //======================================================= 133 | // Binary Search Tree Representing the lines currently crossing the sweep line. 134 | //-------------------------------- 135 | 136 | void TupleBST::addTuple(LineTuple * line_tuple) 137 | { 138 | 139 | set::iterator iter = bst.begin(); 140 | 141 | scrib::Line * line = line_tuple->line; 142 | 143 | while (iter != bst.end()) 144 | { 145 | LineTuple * line_tuple = (*iter); 146 | scrib::Line * other_line = line_tuple->line; 147 | line->intersect(other_line); 148 | iter++; 149 | } 150 | 151 | bst.insert(line_tuple); 152 | return; 153 | 154 | } 155 | 156 | void TupleBST::removeTuple(LineTuple * line_tuple) 157 | { 158 | 159 | set::iterator iter = bst.find(line_tuple); 160 | 161 | // Not Found. 162 | if (iter == bst.end()) 163 | { 164 | cout << "--Tuple Not Found!!! " << line_tuple->x << " " << line_tuple->y << " " << line_tuple->slope << endl; 165 | return; 166 | } 167 | else 168 | { 169 | //cout << "--Tuple Exited: " << line_tuple->x << " " << line_tuple->y << " " << line_tuple->slope << endl; 170 | } 171 | 172 | bst.erase(iter); 173 | 174 | } 175 | 176 | 177 | } 178 | 179 | 180 | -------------------------------------------------------------------------------- /src/Intersector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ofxSweepLine.h 3 | * 4 | * Written by Bryce Summers on 7 - 23 - 2015. 5 | * 6 | * Referenced: https://github.com/alexiswolfish/ofxSweepLine 7 | * 8 | * 9 | * Canonical scrib::Line objects are represented by LineTuple objects in a bst. 10 | * Events along the sweep line are managed by a priority queue. 11 | * 12 | * This implementation should properly handle lines originating from the same end points. 13 | * This implementation assumes that no lines have the same slope and no lines are vertical. 14 | * 15 | * Note : Sets regard types as being equal when their cooresponding ordering operator returns false reflexively. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "ofMain.h" 21 | #include 22 | #include 23 | #include 24 | #include "Line.h" 25 | 26 | namespace scrib { 27 | 28 | // Line Tuples are stored in a binary search tree to 29 | // represent the lines currently crossing the sweep line. 30 | class LineTuple 31 | { 32 | 33 | public: 34 | 35 | LineTuple() 36 | { 37 | line = NULL; 38 | } 39 | 40 | // Used to correctly order the y tuples by y coordinate. 41 | float x; 42 | float y; 43 | 44 | // The slope is mainly used to properly order lines originating from the same end point. 45 | float slope; 46 | 47 | // Every LineTuple is associated with one line. 48 | scrib::Line * line; 49 | }; 50 | 51 | // Used to impose an ordering for the tuples in the bst. 52 | struct LineTupleCompare 53 | { 54 | // Returns true if e1 < e2. 55 | bool operator()(const LineTuple * e1, const LineTuple * e2) const 56 | { 57 | 58 | // Equal. 59 | if (e1 == e2) 60 | { 61 | return false; 62 | } 63 | 64 | if ((e1->y) < (e2->y)) 65 | { 66 | return true; 67 | } 68 | 69 | if ((e2->y) < (e1->y)) 70 | { 71 | return false; 72 | } 73 | 74 | // We want lines with greater slop to be higher in the bst. 75 | return (e1->slope) < (e2->slope); 76 | } 77 | }; 78 | 79 | 80 | // The Binary Search Tree used to store the tuples. 81 | class TupleBST 82 | { 83 | 84 | public: 85 | 86 | TupleBST() {}; 87 | //virtual ~TupleBST(){}; 88 | 89 | // IN the line_tuple. 90 | // OUT : The tuples that are above and below. 91 | void addTuple(LineTuple * line_tuple); 92 | void removeTuple(LineTuple * line_tuple); 93 | 94 | private: 95 | set bst; 96 | 97 | }; 98 | 99 | // These objects represent events along the sweep line. 100 | class Event 101 | { 102 | public: 103 | 104 | Event() 105 | { 106 | tuple1 = NULL; 107 | } 108 | 109 | enum Type { ENTER, EXIT }; 110 | 111 | Type type; 112 | 113 | float x; 114 | float y; 115 | 116 | LineTuple * tuple1; 117 | 118 | }; 119 | 120 | class EventPQ 121 | { 122 | public: 123 | 124 | struct EventCompare 125 | { 126 | // Returns true if e1 < e2. 127 | bool operator()(const Event& e1, const Event& e2) const 128 | { 129 | // Equal. 130 | if (e1.tuple1 == e2.tuple1 && 131 | e1.type == e2.type) 132 | { 133 | return false; 134 | } 135 | 136 | if (e1.x < e2.x) { return true; } 137 | if (e2.x < e1.x) { return false; } 138 | if (e1.y > e2.y) { return true; } 139 | if (e2.y < e1.y) { return false; } 140 | 141 | if ((e1.type == Event::EXIT) && (e2.type == Event::ENTER)) 142 | { 143 | return true; 144 | } 145 | 146 | if ((e1.type == Event::ENTER) && (e2.type == Event::EXIT)) 147 | { 148 | return false; 149 | } 150 | 151 | if ((e1.tuple1->slope) > (e2.tuple1->slope)) 152 | { 153 | return true; 154 | } 155 | 156 | if ((e1.tuple1->slope) < (e2.tuple1->slope)) 157 | { 158 | return true; 159 | } 160 | 161 | 162 | return false; 163 | } 164 | }; 165 | 166 | // -- Constructor. 167 | // Takes a list of the initial lines and adds start and end events for each of them. 168 | EventPQ(std::vector * lines); 169 | virtual ~EventPQ() {}; 170 | 171 | std::set PQ; 172 | 173 | Event delMin(); 174 | bool isEmpty(); 175 | 176 | int size() 177 | { 178 | return PQ.size(); 179 | } 180 | 181 | private: 182 | 183 | void populateEvent(Event &enter, Event &exit, ofPoint &p1, ofPoint &p2, scrib::Line * line); 184 | 185 | }; 186 | 187 | class Intersector 188 | { 189 | 190 | public: 191 | Intersector() {}; 192 | virtual ~Intersector() {}; 193 | 194 | // Calls the Line::intersect method on all intersecting lines. 195 | // Does not treat lines that intersect at common points as intersecting. 196 | void intersect(std::vector * lines); 197 | void intersect_brute_force(std::vector * lines); 198 | }; 199 | 200 | } 201 | -------------------------------------------------------------------------------- /src/Line.cpp: -------------------------------------------------------------------------------- 1 | #include "Line.h" 2 | 3 | namespace scrib { 4 | 5 | Line::Line(int start_point, int end_point, std::vector * points_global) 6 | { 7 | p1_index = start_point; 8 | p2_index = end_point; 9 | 10 | // The indices point to points within this global array. 11 | points = points_global; 12 | 13 | p1 = points->at(p1_index); 14 | p2 = points->at(p2_index); 15 | 16 | offset = p2 - p1; 17 | } 18 | 19 | Line::~Line() 20 | { 21 | //dtor 22 | } 23 | 24 | // Returns true iff the lines intersect, if they are not already connected at endpoints a split point is created. 25 | // Intersections at end points --> a false return; 26 | bool Line::intersect(Line * other) 27 | { 28 | 29 | // Already Previously Connected. 30 | // Connected at a joint in the input polyline. 31 | if (p1_index == other->p1_index || p1_index == other->p2_index || 32 | p2_index == other->p1_index || p2_index == other->p2_index) 33 | { 34 | return false; 35 | } 36 | 37 | // No intersection. 38 | if (!detect_intersection(other)) 39 | { 40 | return false; 41 | } 42 | 43 | report_intersection(other); 44 | return true; 45 | 46 | } 47 | 48 | // Returns -1 on one side of the line. 49 | // Returns 0 if the point is on the line. 50 | // Returns 1 if the point is on the other side of the line. 51 | float Line::line_side_test(ofPoint c) 52 | { 53 | return ((p2.x - p1.x)*(c.y - p1.y) - (p2.y - p1.y)*(c.x - p1.x)); 54 | } 55 | 56 | inline bool Line::detect_intersection(Line * other) 57 | { 58 | float a1 = line_side_test(other->p1); 59 | float a2 = line_side_test(other->p2); 60 | 61 | float b1 = other->line_side_test(p1); 62 | float b2 = other->line_side_test(p2); 63 | 64 | /* The product of two point based line side tests will be negative iff 65 | * the points are not on strictly opposite sides of the line. 66 | * If the product is 0, then at least one of the points is on the line not containing the points. 67 | */ 68 | return a1*a2 <= 0 && b1*b2 <= 0; 69 | } 70 | 71 | inline void Line::report_intersection(Line * other) 72 | { 73 | 74 | // Find the intersection point. 75 | 76 | /* 77 | u = ((bs.y - as.y) * bd.x - (bs.x - as.x) * bd.y) / (bd.x * ad.y - bd.y * ad.x) 78 | v = ((bs.y - as.y) * ad.x - (bs.x - as.x) * ad.y) / (bd.x * ad.y - bd.y * ad.x) 79 | Factoring out the common terms, this comes to: 80 | 81 | dx = bs.x - as.x 82 | dy = bs.y - as.y 83 | det = bd.x * ad.y - bd.y * ad.x 84 | u = (dy * bd.x - dx * bd.y) / det 85 | v = (dy * ad.x - dx * ad.y) / det 86 | */ 87 | 88 | // Extract the relevant points. 89 | ofPoint as = p1; 90 | ofPoint bs = other->p1; 91 | ofPoint ad = offset; 92 | ofPoint bd = other->offset; 93 | 94 | float dx = bs.x - as.x; 95 | float dy = bs.y - as.y; 96 | float det = bd.x * ad.y - bd.y * ad.x; 97 | float u = (dy * bd.x - dx * bd.y) / det; 98 | float v = (dy * ad.x - dx * ad.y) / det; 99 | 100 | // The intersection is at time coordinates u and v. 101 | // Note: Time is relative to the offsets, so p1 = time 0 and p2 is time 1. 102 | 103 | // u is the time coordinate for this line. 104 | split_points_per.push_back(u); 105 | 106 | // v is the time coordinate for the other line. 107 | other->split_points_per.push_back(v); 108 | 109 | ofPoint intersection_point = as + ad*u; 110 | 111 | // Get the next index that will be used to store the newly created point. 112 | int index = points->size(); 113 | points->push_back(intersection_point); 114 | 115 | split_points_indices.push_back(index); 116 | other->split_points_indices.push_back(index); 117 | } 118 | 119 | /* 120 | bool isLeft(Point a, Point b, Point c){ 121 | return ((b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x)) > 0; 122 | } 123 | */ 124 | 125 | 126 | // Appends all of the lines that are used to subdivide this one, 127 | // complete with proper and consistent indices into the global array. 128 | void Line::getSplitLines(std::vector * lines_collector) 129 | { 130 | // Number of split points. 131 | int len = split_points_per.size(); 132 | 133 | // Not split points. 134 | if (len == 0) 135 | { 136 | // Saves work. 137 | lines_collector->push_back(*this); 138 | return; 139 | } 140 | 141 | // First sort points. 142 | sort_sub_points(); 143 | 144 | // Make sure the last line is pushed. 145 | // This ensures that that initial line will be pushed if this line has no intersections. 146 | split_points_indices.push_back(p2_index); 147 | 148 | // Append all of the line's segments to the inputted array. 149 | int last_indice = split_points_indices[0]; 150 | 151 | // The initial line. 152 | lines_collector->push_back(scrib::Line(p1_index, last_indice, points)); 153 | 154 | for (int i = 1; i < len; i++) 155 | { 156 | int next_indice = split_points_indices[i]; 157 | lines_collector->push_back(scrib::Line(last_indice, next_indice, points)); 158 | last_indice = next_indice; 159 | } 160 | 161 | // The last line. 162 | lines_collector->push_back(scrib::Line(last_indice, p2_index, points)); 163 | 164 | // Done. 165 | return; 166 | 167 | } 168 | 169 | // Sorts all of the subpoints by percentage. 170 | void Line::sort_sub_points() 171 | { 172 | 173 | int len = split_points_per.size(); 174 | 175 | // Insertion sort. 176 | for (int i = 1; i < len; i++) 177 | for (int i2 = i - 1; i2 >= 0; i2--) 178 | { 179 | int i1 = i2 + 1; 180 | 181 | if (split_points_per[i2] <= split_points_per[i1]) 182 | { 183 | break; 184 | } 185 | 186 | 187 | // -- Swap at indices i2 and i2 + 1. 188 | // Keep the percentage measuremtents consistent with the indices. 189 | float temp_f = split_points_per[i2]; 190 | split_points_per[i2] = split_points_per[i1]; 191 | split_points_per[i1] = temp_f; 192 | 193 | int temp_i = split_points_indices[i2]; 194 | split_points_indices[i2] = split_points_indices[i1]; 195 | split_points_indices[i1] = temp_i; 196 | } 197 | 198 | return; 199 | 200 | } 201 | 202 | ofPoint Line::getLatestIntersectionPoint() 203 | { 204 | return points->at(points->size() - 1); 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /src/Line.h: -------------------------------------------------------------------------------- 1 | #ifndef LINE_H 2 | #define LINE_H 3 | 4 | #include 5 | #include "ofMain.h" 6 | 7 | /** 8 | * Line Representation and methods class. 9 | * First Draft completed by Bryce Summers on 7/14/2015. 10 | */ 11 | 12 | namespace scrib 13 | { 14 | 15 | class Line 16 | { 17 | public: 18 | 19 | // Constructor. 20 | Line(int start_point_index, int end_point_index, std::vector * points_global); 21 | virtual ~Line(); 22 | 23 | /** Intersects the given line with this line. 24 | * Adds a split point if they do intersect. 25 | * Any created split points are added to the referenced global collection of points. 26 | */ 27 | bool intersect(Line * other); 28 | 29 | // Returns a signed floating point number indicating which direction the given point is relative to this line. 30 | float line_side_test(ofPoint p); 31 | 32 | // Appends all of the split set of lines in order to the output vector. 33 | // Adds itself if it does not contain any split lines. 34 | // Line pts are oriented along the polyline, such that p1 comes before p2 in the polyline + intersection point ordering. 35 | void getSplitLines(std::vector * lines_collector); 36 | 37 | // This function should only be called after a call to intersect has returned true. 38 | ofPoint getLatestIntersectionPoint(); 39 | 40 | // Point indices. 41 | int p1_index; 42 | int p2_index; 43 | 44 | // Actual points. 45 | ofPoint p1; 46 | ofPoint p2; 47 | 48 | // The offset between the two points. 49 | ofPoint offset; 50 | 51 | 52 | protected: 53 | private: 54 | 55 | // The canonical array of points. 56 | std::vector * points; 57 | // Collection of doubles representing the percentage a point is between p1 and p2. 58 | std::vector split_points_per; 59 | // The indices of the points. 60 | std::vector split_points_indices; 61 | 62 | void sort_sub_points(); 63 | 64 | // Returns true iff this line segment intersects with the other line segment. 65 | inline bool detect_intersection(Line * other); 66 | inline void report_intersection(Line * other); 67 | 68 | }; 69 | 70 | } 71 | 72 | #endif // LINE_H 73 | -------------------------------------------------------------------------------- /src/OffsetCurves.cpp: -------------------------------------------------------------------------------- 1 | #include "OffsetCurves.h" 2 | 3 | namespace scrib 4 | { 5 | 6 | OffsetCurves::OffsetCurves() 7 | { 8 | //ctor 9 | } 10 | 11 | OffsetCurves::~OffsetCurves() 12 | { 13 | //dtor 14 | } 15 | 16 | std::vector * OffsetCurves::computeOffsetCurve(std::vector * input, double dist) 17 | { 18 | cout << input->size() << endl; 19 | 20 | if (input->size() == 0) 21 | { 22 | std::vector * trivial_output = new std::vector(); 23 | return trivial_output; 24 | } 25 | 26 | if (input->size() == 1) 27 | { 28 | std::vector * circle_output = new std::vector(); 29 | 30 | cout << "size1" << endl; 31 | 32 | int len = 3; 33 | for (int i = 0; i < 3; i++) 34 | { 35 | circle_output->push_back(point_info(ofPoint(dist*cos(i / (PI * 2)), dist*sin(i / (PI * 2))), i)); 36 | } 37 | 38 | 39 | return circle_output; 40 | } 41 | 42 | if (scrib::computeAreaOfPolygon(input) > 0) 43 | { 44 | dist *= -1; 45 | } 46 | 47 | std::vector * unpruned_output; 48 | unpruned_output = offsetCurve(input, dist); 49 | std::vector * pruned_output = computeExterior(unpruned_output); 50 | return pruned_output; 51 | } 52 | 53 | std::vector * OffsetCurves::offsetCurve(std::vector * input, double dist) 54 | { 55 | std::vector perp_dirs; 56 | int len = input->size(); 57 | for (int i = 0; i < len; i++) 58 | { 59 | int p0 = (i + len - 1) % len; 60 | int p1 = i; 61 | int p2 = (i + 1) % len; 62 | 63 | ofPoint point2 = input->at(p2); 64 | ofPoint point0 = input->at(p0); 65 | 66 | ofPoint tangent = point2 - point0; 67 | tangent.normalize(); 68 | ofPoint perpendicular = ofVec2f(-tangent.y, tangent.x); 69 | perp_dirs.push_back(perpendicular); 70 | } 71 | 72 | std::vector * output = new std::vector(); 73 | 74 | for (int i = 0; i < len; i++) 75 | { 76 | ofPoint original = input->at(i); 77 | ofPoint perp = perp_dirs[i]; 78 | output->push_back(original + perp*dist); 79 | } 80 | 81 | return output; 82 | } 83 | 84 | // Prunes a curve of intermediary loops. Returns the curve consisting soley of its exterior. 85 | std::vector * OffsetCurves::computeExterior(std::vector * input) 86 | { 87 | scrib::FaceFinder segmenter; 88 | 89 | // Make sure the segmenter interprets the line as a closed loop. 90 | segmenter.setClosed(true); 91 | 92 | std::vector< std::vector *> * faces; 93 | faces = segmenter.FindFaces(input); 94 | 95 | PolylineGraphPostProcessor post; 96 | post.load_face_vector(faces); 97 | 98 | std::vector external_face_indices; 99 | post.determineComplementedFaces(&external_face_indices); 100 | 101 | // Get the index of the 1 external face. 102 | int output_index = external_face_indices[0]; 103 | std::vector * output = faces->at(output_index); 104 | 105 | // Deallocate superfluous face curves. 106 | int len = faces->size(); 107 | for (int i = 0; i < len; i++) 108 | { 109 | if (i != output_index) 110 | { 111 | delete faces->at(i); 112 | } 113 | } 114 | 115 | delete faces; 116 | 117 | return output; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/OffsetCurves.h: -------------------------------------------------------------------------------- 1 | #ifndef OFFSETCURVES_H 2 | #define OFFSETCURVES_H 3 | 4 | #include 5 | #include "ofMain.h" 6 | #include "Line.h" 7 | #include "Intersector.h" 8 | #include "FaceFinder.h" 9 | 10 | 11 | /* 12 | * Offset Curve computer. 13 | * Written by Bryce Summers on 2/19/2016. 14 | * Purpose: For a given *closed* curve, 15 | * 16 | * this computes the curve defined by all points at a signed distance d away form the curve. 17 | * 18 | * Features: A solution to the problem through a reduction to the external face of the planar graph segmentation. 19 | */ 20 | 21 | namespace scrib { 22 | 23 | class OffsetCurves 24 | { 25 | public: 26 | OffsetCurves(); 27 | virtual ~OffsetCurves(); 28 | 29 | std::vector * computeOffsetCurve(std::vector * inputs, double dist); 30 | 31 | std::vector * offsetCurve(std::vector * inputs, double dist); 32 | 33 | // Takes a closed loop and returns the closed loop cooresponding to its exterior. 34 | std::vector * computeExterior(std::vector * inputs); 35 | 36 | }; 37 | 38 | } 39 | 40 | #endif // OFFSETCURVES_H 41 | -------------------------------------------------------------------------------- /src/PolylineGraphData.cpp: -------------------------------------------------------------------------------- 1 | #include "PolylineGraphData.h" 2 | 3 | namespace scrib 4 | { 5 | 6 | void Graph_Data::clearFaceMarks() 7 | { 8 | for (Face_Iter iter = graph->facesBegin(); iter != graph->facesEnd(); iter++) 9 | { 10 | (*iter) -> data -> marked = false; 11 | } 12 | } 13 | 14 | void Graph_Data::clearVertexMarks() 15 | { 16 | for (Vertex_Iter iter = graph->verticesBegin(); iter != graph->verticesEnd(); iter++) 17 | { 18 | (*iter) -> data -> marked = false; 19 | } 20 | } 21 | 22 | void Graph_Data::clearEdgeMarks() 23 | { 24 | for (Edge_Iter iter = graph->edgesBegin(); iter != graph->edgesEnd(); iter++) 25 | { 26 | (*iter) -> data -> marked = false; 27 | } 28 | } 29 | 30 | void Graph_Data::clearHalfedgeMarks() 31 | { 32 | for (Halfedge_Iter iter = graph->halfedgesBegin(); iter != graph->halfedgesEnd(); iter++) 33 | { 34 | (*iter) -> data -> marked = false; 35 | } 36 | } 37 | 38 | void Graph_Data::clearMarks() 39 | { 40 | clearFaceMarks(); 41 | clearVertexMarks(); 42 | clearEdgeMarks(); 43 | clearHalfedgeMarks(); 44 | } 45 | } -------------------------------------------------------------------------------- /src/PolylineGraphData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | #include "HalfedgeGraph.h" 5 | 6 | namespace scrib 7 | { 8 | // Forward Declaration of Associated Data Objects. 9 | class Graph_Data; 10 | class Vertex_Data; 11 | class Face_Data; 12 | class Halfedge_Data; 13 | class Edge_Data; 14 | 15 | class Graph_Data 16 | { 17 | private: Graph * graph; 18 | 19 | public: 20 | 21 | Graph_Data(Graph * graph) 22 | { 23 | this -> graph = graph; 24 | } 25 | 26 | void clearFaceMarks(); 27 | void clearVertexMarks(); 28 | void clearEdgeMarks(); 29 | void clearHalfedgeMarks(); 30 | 31 | void clearMarks(); 32 | 33 | }; 34 | 35 | class Face_Data 36 | { 37 | private: 38 | Face * face; 39 | 40 | public: 41 | 42 | Face_Data(Face * face) 43 | { 44 | this -> face = face; 45 | } 46 | 47 | bool marked = false; 48 | 49 | std::vector hole_representatives; 50 | 51 | void addHole(Face * hole) 52 | { 53 | hole_representatives.push_back(hole); 54 | } 55 | 56 | // The area of the face is determined by the intersection this face with all of the hole faces, 57 | // which will be specified by exterior facing edge that enlose an infinite complemented area. 58 | }; 59 | 60 | class Vertex_Data 61 | { 62 | private: 63 | 64 | Vertex * vertex; 65 | 66 | public: 67 | 68 | Vertex_Data(Vertex * vertex) 69 | { 70 | this->vertex = vertex; 71 | } 72 | 73 | ofPoint point; 74 | bool marked = false; 75 | 76 | bool tail_point = false; 77 | 78 | // Labels Vertices that have more than two outgoing edges. 79 | bool intersection_point = false; 80 | 81 | // FIXME: Remove this if it is just taking up unneeded space. 82 | bool singleton_point = false; 83 | 84 | bool isExtraordinary() 85 | { 86 | return tail_point || intersection_point; 87 | } 88 | 89 | // Used as a temporary structure for graph construction, but it is also may be relevant to users. 90 | // I don't know whether I will maintain this structure outside of graph construction. 91 | // FIXME: I might switch this to being a pointer to allow for me to null it out when no longer needed. 92 | std::vector outgoing_edges; 93 | }; 94 | 95 | class Edge_Data 96 | { 97 | private: 98 | 99 | // Halfedge Data members have pointers to their connectivity element. 100 | Edge * edge; 101 | 102 | public: 103 | 104 | Edge_Data(Edge * edge) 105 | { 106 | this->edge = edge; 107 | } 108 | 109 | bool marked = false; 110 | }; 111 | 112 | class Halfedge_Data 113 | { 114 | 115 | private: 116 | 117 | // Halfedge Data members have pointers to their connectivity element. 118 | Halfedge * halfedge; 119 | 120 | public: 121 | 122 | Halfedge_Data(Halfedge * halfedge) 123 | { 124 | this->halfedge = halfedge; 125 | } 126 | 127 | 128 | bool marked = false; 129 | Halfedge * next_extraordinary = NULL; 130 | 131 | // A Halfedge will be labeled as extraordinary iff its vertex is an intersection point or a tail_point. 132 | bool isExtraordinary() 133 | { 134 | return halfedge->vertex->data->isExtraordinary(); 135 | } 136 | }; 137 | } -------------------------------------------------------------------------------- /src/PolylineGraphEmbedder.cpp: -------------------------------------------------------------------------------- 1 | #include "PolylineGraphEmbedder.h" 2 | 3 | namespace scrib { 4 | 5 | void PolylineGraphEmbedder::setClosed(bool isClosed) 6 | { 7 | closed_loop = isClosed; 8 | } 9 | 10 | // FIXME: Allow for the embedding of multiple trivial subgraphs. 11 | 12 | Graph * PolylineGraphEmbedder::embedPolylineSet(std::vector< std::vector *> * inputs) 13 | { 14 | // Make sure that the previous data is cleared. 15 | int len = inputs->size(); 16 | for (int i = 0; i < len; i++) 17 | { 18 | loadInput(inputs->at(i)); 19 | } 20 | 21 | return do_the_rest(); 22 | } 23 | 24 | Graph * PolylineGraphEmbedder::embedPolyline(std::vector * inputs) 25 | { 26 | // Handle Trivial Input. 27 | if (inputs->size() <= 1) 28 | { 29 | return trivial(inputs); 30 | } 31 | 32 | // Make sure that the previous data is cleared. 33 | loadInput(inputs); 34 | 35 | return do_the_rest(); 36 | } 37 | 38 | inline Graph * PolylineGraphEmbedder::trivial(std::vector * inputs) 39 | { 40 | graph = newGraph(); 41 | 42 | if (inputs->size() < 1) 43 | { 44 | return graph; // Trivial empty Graph. 45 | } 46 | 47 | // 1 point Graph. 48 | 49 | // We construct one of each element for the singleton graph. 50 | // NOTE: This allocation is a wrapper on top of the Graph allocation function, which allocates its Vertex_Data object. 51 | // the other functions this->new[ ____ ] work in the same way. 52 | Vertex * vertex = newVertex(); 53 | Vertex_Data * vertex_data = vertex -> data; 54 | Edge * edge = newEdge(); 55 | 56 | Face * interior = newFace(); 57 | Face * exterior = newFace(); 58 | Face_Data * interior_data = interior -> data; 59 | Face_Data * exterior_data = exterior -> data; 60 | 61 | Halfedge * halfedge = newHalfedge(); 62 | Halfedge * twin = newHalfedge();// Somewhat fake, since singleton graphs are degenerate. 63 | 64 | 65 | vertex_data->point = inputs -> at(0); 66 | 67 | vertex -> halfedge = halfedge; 68 | edge -> halfedge = halfedge; 69 | 70 | // The interior is trivial and is defined by a trivial internal and external null area point boundary. 71 | interior -> halfedge = halfedge; 72 | interior_data -> addHole(exterior); 73 | 74 | exterior -> halfedge = halfedge; 75 | 76 | // Self referential exterior loop. 77 | halfedge -> edge = edge; 78 | halfedge -> face = exterior; 79 | halfedge -> next = halfedge; 80 | halfedge -> prev = halfedge; 81 | halfedge -> twin = twin; 82 | halfedge -> vertex = vertex; 83 | 84 | // Self referential interior loop. 85 | twin -> edge = edge; 86 | twin -> face = interior; 87 | twin -> next = twin; 88 | twin -> prev = twin; 89 | twin -> twin = halfedge; 90 | twin -> vertex = vertex; 91 | 92 | return graph; 93 | } 94 | 95 | void PolylineGraphEmbedder::loadInput(std::vector * inputs) 96 | { 97 | // Populate the original points. 98 | int len = inputs -> size(); 99 | 100 | // The offset is the initial index of the first input point. 101 | // We can therefore load multiple input lines and keep the indices distinct. 102 | int offset = points.size(); 103 | 104 | for (int i = 0; i < len; i++) 105 | { 106 | ofPoint input_point = inputs -> at(i) + ofPoint(ofRandomf(), ofRandomf()); 107 | 108 | // A Paranoid vertical line prevention technique. 109 | if ((offset > 0 || i > 0) && points[offset + i - 1].x == input_point.x) 110 | { 111 | input_point.x += .001; 112 | } 113 | 114 | points.push_back(input_point); 115 | } 116 | 117 | // Populate the original lines. 118 | for (int i = 0; i < len - 1; i++) 119 | { 120 | lines_initial.push_back(scrib::Line(i + offset, i + offset + 1, &points)); 121 | } 122 | 123 | // Add a line connecting the first and last points on the original set of input points if 124 | // the face finder is in closed loop mode. 125 | // In other words put a duplicate copy of the initial point. 126 | if (closed_loop) 127 | { 128 | // connects last point at index (len - 1 + offset) to the first point, located at index (0 + offset). 129 | lines_initial.push_back(scrib::Line(len - 1 + offset, 0 + offset, &points)); 130 | } 131 | 132 | } 133 | 134 | inline Graph * PolylineGraphEmbedder::do_the_rest() 135 | { 136 | // ASSUMPTION: Step 1. Input Loading has been acomplished. 137 | // We should have a list of indexed points and index associated edges. 138 | 139 | splitIntersectionPoints(); 140 | allocate_graph_from_input(); 141 | sort_outgoing_edges_by_angle(); 142 | associate_halfedge_cycles(); 143 | 144 | Graph * output = deriveFaces(); 145 | 146 | cleanup(); 147 | 148 | return output; 149 | } 150 | 151 | void PolylineGraphEmbedder::splitIntersectionPoints() 152 | { 153 | scrib::Intersector intersector; 154 | 155 | // Use a custom made O(maximum vertical overlap * log(maximum vertical overlap). 156 | // Very small constant factors, cache friendly. 157 | if (bUseFastAlgo) 158 | { 159 | intersector.intersect(&lines_initial); 160 | } 161 | else 162 | { 163 | // Naive brute force algo. 164 | // N^2. Small constants. As robust as it gets. 165 | intersector.intersect_brute_force(&lines_initial); 166 | } 167 | 168 | // Populate the split sequence of lines. 169 | lines_split.clear(); 170 | 171 | int numLines = lines_initial.size(); 172 | 173 | // Populates the list of edge disjoint lines that only intersect at vertices. 174 | // puts the edge in consecutive order following the input polylines. 175 | for (int i = 0; i < numLines; i++) 176 | { 177 | scrib::Line line = lines_initial[i]; 178 | line.getSplitLines(&lines_split); 179 | } 180 | } 181 | 182 | void PolylineGraphEmbedder::allocate_graph_from_input() 183 | { 184 | graph = newGraph(); 185 | 186 | // -- Allocate all Vertices and their outgoing halfedge temporary structure. 187 | int len = points.size(); 188 | for (int i = 0; i < len; i++) 189 | { 190 | Vertex * vert = newVertex(); 191 | Vertex_Data * vert_data = vert -> data; 192 | 193 | vert -> halfedge = NULL; 194 | vert_data -> point = points[i]; 195 | } 196 | 197 | // -- Allocate 2 halfedges and 1 full edge for ever line in the split input. 198 | len = lines_split.size(); 199 | for (int i = 0; i < len; i++) 200 | { 201 | newHalfedge(); 202 | newHalfedge(); 203 | newEdge(); 204 | } 205 | 206 | // Associate edges <-> halfedges. 207 | // halfedges <-> twin halfedges. 208 | // halfedges <-> vertices. 209 | Halfedge * last_forwards_halfedge = NULL; 210 | Halfedge * last_backwards_halfedge = NULL; 211 | int last_index = len * 2 - 1; 212 | for (int i = 0; i < len; i++) 213 | { 214 | Line & line = lines_split[i]; 215 | int vertex_ID = line.p1_index; 216 | int vertex_twin_ID = line.p2_index; 217 | int edge_ID = i; 218 | int halfedge_ID = i; // Forwards halfedges with regards to the polyline. 219 | int twin_ID = last_index - i;// Backwards halfedges. 220 | 221 | Edge * edge = graph -> getEdge(edge_ID); 222 | Halfedge * halfedge = graph -> getHalfedge(halfedge_ID); // Forwards facing. 223 | Halfedge * twin = graph -> getHalfedge(twin_ID); // Backwards facing. 224 | Vertex * vert = graph -> getVertex(vertex_ID); 225 | Vertex * vert_twin = graph -> getVertex(vertex_twin_ID); 226 | 227 | Vertex_Data * vert_data = vert -> data; 228 | Vertex_Data * vert_twin_data = vert_twin -> data; 229 | 230 | // Edge <--> Halfedge. 231 | edge -> halfedge = halfedge; 232 | halfedge -> edge = edge; 233 | twin -> edge = edge; 234 | 235 | // Halfedge <--> twin Halfedges. 236 | halfedge -> twin = twin; 237 | twin -> twin = halfedge; 238 | 239 | // Halfedge <--> Vertex. 240 | 241 | halfedge -> vertex = vert; 242 | twin -> vertex = vert_twin; 243 | 244 | // Here we guranteed that Halfedge h->vertex->halfedge = h iff 245 | // the halfedge is the earliest halfedge originating from the vertex in the order. 246 | 247 | // FIXME: This no longer seems necessary, because of the outgoing edge structure. 248 | // Desired properties may be maintained at a later step. 249 | 250 | if (vert -> halfedge == NULL) 251 | { 252 | vert -> halfedge = halfedge; 253 | } 254 | 255 | if (vert_twin -> halfedge == NULL) 256 | { 257 | vert_twin -> halfedge = twin; 258 | } 259 | 260 | // -- We store outgoing halfedges for each vertex in a temporary outgoing edges structure. 261 | vert_data -> outgoing_edges.push_back(halfedge); 262 | vert_twin_data -> outgoing_edges.push_back(twin); 263 | } 264 | } 265 | 266 | void PolylineGraphEmbedder::sort_outgoing_edges_by_angle() 267 | { 268 | // Sort each outgoing edges list. 269 | Vertex_Iter start = graph -> verticesBegin(); 270 | Vertex_Iter end = graph -> verticesEnd(); 271 | for (Vertex_Iter iter = start; iter != end; iter++) 272 | { 273 | Vertex_Data * vert_data = (*iter) -> data; 274 | std::vector & outgoing_edges = vert_data -> outgoing_edges; 275 | sort_outgoing_edges(outgoing_edges); 276 | } 277 | } 278 | 279 | void PolylineGraphEmbedder::sort_outgoing_edges(std::vector & outgoing_edges) 280 | { 281 | // Initialize useful information. 282 | int len = outgoing_edges.size(); 283 | 284 | // Less than 2 are already sorted, regardless of orientation. 285 | if (len <= 2) 286 | { 287 | return; 288 | } 289 | 290 | // Note: len == 3 is sorted, but possibly of the wrong orientation. 291 | 292 | std::vector angles; 293 | 294 | // Extract central information. 295 | Halfedge * outgoing_halfedge_representative = outgoing_edges[0]; 296 | Vertex * center_vert = outgoing_halfedge_representative -> vertex; 297 | Vertex_Data * center_data = center_vert -> data; 298 | ofPoint center_point = center_data -> point; 299 | 300 | // Populate the angles array with absolute relative angles. 301 | for (auto iter = outgoing_edges.begin(); iter != outgoing_edges.end(); iter++) 302 | { 303 | Halfedge * out = *iter; 304 | Halfedge * in = out -> twin; 305 | Vertex * outer_vert = in -> vertex; 306 | Vertex_Data * outer_data = outer_vert -> data; 307 | ofPoint outer_point = outer_data -> point; 308 | 309 | float angle = atan2(outer_point.y - center_point.y, 310 | outer_point.x - center_point.x); 311 | angles.push_back(angle); 312 | } 313 | 314 | // Insertion sort based on the angles. 315 | for (int i = 1; i < len; i++) 316 | { 317 | for (int i2 = i - 1; i2 >= 0; i2--) 318 | { 319 | int i1 = i2 + 1; 320 | 321 | if (angles[i2] <= angles[i1]) 322 | { 323 | break; 324 | } 325 | 326 | // -- Swap at indices i2 and i2 + 1. 327 | // Keep the angle measurements synchronized with the halfedges. 328 | float temp_f = angles.at(i2); 329 | angles[i2] = angles[i1]; 330 | angles[i1] = temp_f; 331 | 332 | Halfedge * temp_he = outgoing_edges[i2]; 333 | outgoing_edges[i2] = outgoing_edges[i1]; 334 | outgoing_edges[i1] = temp_he; 335 | } 336 | } 337 | 338 | } 339 | 340 | void PolylineGraphEmbedder::associate_halfedge_cycles() 341 | { 342 | Vertex_Iter start = graph -> verticesBegin(); 343 | Vertex_Iter end = graph -> verticesEnd(); 344 | for (Vertex_Iter vert = start; vert != end; vert++) 345 | { 346 | Vertex_Data * vert_data = (*vert) -> data; 347 | vector outgoing_edges = vert_data -> outgoing_edges; 348 | int degree = outgoing_edges.size(); 349 | 350 | // Singleton point. 351 | if (degree == 0) 352 | { 353 | vert_data -> singleton_point = true; 354 | 355 | Halfedge * halfedge = (*vert) -> halfedge; 356 | // ASSERTION: halfedge != null. If construction the user inputs a graph with singleton points. 357 | // FIXME: Perhaps I should allocate the half edge here for the trivial case. Maybe I should combine the 358 | // places in my code where I define the singleton state. 359 | 360 | halfedge -> next = halfedge; 361 | halfedge -> prev = halfedge; 362 | continue; 363 | } 364 | 365 | // Tail vertex. 366 | if (degree == 1) 367 | { 368 | vert_data -> tail_point = true; 369 | 370 | Halfedge * out = (*vert) -> halfedge; 371 | Halfedge * in = out -> twin; 372 | 373 | out -> prev = in; 374 | in -> next = out; 375 | continue; 376 | } 377 | 378 | // Mark junction points. 379 | if (degree > 2) 380 | { 381 | vert_data -> intersection_point = true; 382 | } 383 | 384 | // Link the halfedge neighborhood. 385 | for (int i = 0; i < degree; i++) 386 | { 387 | Halfedge * out = outgoing_edges[i]; 388 | Halfedge * in = out -> twin; 389 | 390 | // This combined with the sort order determines the consistent orientation. 391 | // I think that it defines a clockwise orientation, but I could be wrong. 392 | 393 | // FIXME: There is something wrong about this ordering. 394 | 395 | in -> next = outgoing_edges[(i + 1) % degree]; 396 | out -> prev = outgoing_edges[(i + degree - 1) % degree] -> twin; 397 | } 398 | 399 | continue; 400 | } 401 | } 402 | 403 | Graph * PolylineGraphEmbedder::deriveFaces() 404 | { 405 | 406 | // For each halfedge, output its cycle once. 407 | 408 | Halfedge_Iter start = graph -> halfedgesBegin(); 409 | Halfedge_Iter end = graph -> halfedgesEnd(); 410 | 411 | // Iterate through all originating points. 412 | for (Halfedge_Iter halfedge = start; halfedge != end; halfedge++) 413 | { 414 | Halfedge_Data * halfedge_data = (*halfedge) -> data; 415 | 416 | // Avoid previously traced cycles. 417 | if (halfedge_data -> marked) 418 | { 419 | continue; 420 | } 421 | 422 | Face * face = newFace(); // GraphEmbedder::newFace() ... 423 | 424 | face -> halfedge = *halfedge; 425 | trace_face(face); 426 | } 427 | 428 | // Clear the marks. 429 | graph -> data -> clearHalfedgeMarks(); 430 | 431 | return this -> graph; 432 | } 433 | 434 | // Isn't this nice and conscise? 435 | void PolylineGraphEmbedder::trace_face(Face * face) 436 | { 437 | Halfedge * start = face -> halfedge; 438 | Halfedge * current = start; 439 | 440 | do 441 | { 442 | current -> face = face; 443 | current -> data -> marked = true; 444 | current = current -> next; 445 | } while (current != start); 446 | } 447 | 448 | void PolylineGraphEmbedder::cleanup() 449 | { 450 | // Remove the previous data. 451 | 452 | // Erase the stack class allocated memory. 453 | points.clear(); 454 | lines_initial.clear(); 455 | lines_split.clear(); 456 | 457 | // No dynamic allocated temporary structures! Wahoo! 458 | } 459 | 460 | } 461 | -------------------------------------------------------------------------------- /src/PolylineGraphEmbedder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Transforms a set of input polylines into a planar graph embedding. 5 | * 6 | * Written by Bryce Summers. 7 | * 8 | * 8/16/2016: Written as a more fully advanced version of FaceFinder.h, 9 | * which outputs sophisticated graph structures oozing with useful connectivity information. 10 | * 11 | * Written for the STUDIO for Creative Inquiry at Carnegie Mellon University. 12 | */ 13 | 14 | // FIXME: I am refactoring post proccess operations into their own class. 15 | 16 | /* -- Here is the interface for calling the built in algorithms for the Scribble segmenter. 17 | * These algorithms include: 18 | * Preprocessing: 19 | * [EMPTY] 20 | * 21 | * Main Algorithm: 22 | * 1. The main algorithm for embedding a set of polylines in space and determining the set of non chordal cycles in the 23 | * associated embedded planar graph. 24 | * 25 | * - A polygon is closed if it has identical starting and ending points and open otherwise. 26 | * The algorithm may be configured to output either open or closed polygons based on the closed_loop mode state. 27 | * 28 | * FIXME: If a user draws a second line completely around an original line, then their will be faces defined by both an external 29 | * face on the original polyline embedding and an internal face on the new enclosing embedding. 30 | * This may invalidate some users' assumptions of a global planar graph embedding without any holes. 31 | * 32 | * Post Processing: 33 | * 1. Determine internal and external faces. (Initial Release) 34 | * 2. Determine trivial and non trivial area faces according to a constant area threshold value. (8/11/2016) 35 | * (If you can't think of any good constant values, you might want to look at the field of 36 | * Topological Data Analysis and their 'barcode' concept: https://en.wikipedia.org/wiki/Topological_data_analysis. 37 | * 3. Clipping off tails, i.e. portions of faces that enclose 0 area. (8/11/2016) 38 | * This could potentially be put into the getCycle function, but I think that it is best to make this a dedicated post processing step instead 39 | * in order to preserve the simplicity of the main algorithm. 40 | * This algorithm properly handles faces with either duplicated or non-duplicated starting and ending points. 41 | * i.e. Those produced in open and closed mode. 42 | */ 43 | 44 | 45 | 46 | // FIXME: Write a list of all of the relevant interesting properties of the my planar embedding implementation. 47 | 48 | // Edges alwas point to the forward faceing halfedge. 49 | // forward facing half edges are consecutively ordered in the first half. 50 | // backwards facing half edges are consecutively ordered in the reverse order of the first half. 51 | 52 | 53 | 54 | 55 | #include 56 | #include "ofMain.h" 57 | #include "Line.h" 58 | #include "Intersector.h" 59 | #include "HalfedgeGraph.h" 60 | #include "PolylineGraphData.h" 61 | 62 | namespace scrib { 63 | 64 | 65 | class PolylineGraphEmbedder 66 | { 67 | public: 68 | 69 | // A User can explicitly pass false to force the intersection points to be found using a brute force algorithm that 70 | // may potentially be more robust and reliable than the optimized intersection algorithm, 71 | // but it kills the performance. 72 | PolylineGraphEmbedder(bool useFastAlgo = true) 73 | { 74 | bUseFastAlgo = useFastAlgo; 75 | closed_loop = false; 76 | }; 77 | virtual ~PolylineGraphEmbedder() {}; 78 | 79 | // Tells this face finder to interpret the input curve as a line if open and a closed loop if closed. 80 | // If close, it will consider endpoints as attached to each other. 81 | void setClosed(bool isClosed); 82 | 83 | // Derives a planar graph embedding from the given input polyline. 84 | // The input will be interpretted as open or closed depending on the value of this.closed_loop; 85 | // Assumes all points are distinct. 86 | // Offsets all input points by a small random amount to prevent degeneracies from vertical edges. 87 | Graph * embedPolyline(std::vector * inputs); 88 | 89 | // Derive faces from a set list of vertex disjoint polyline inputs. 90 | Graph * embedPolylineSet(std::vector< std::vector *> * inputs); 91 | 92 | protected: 93 | private: 94 | 95 | // The trivial function constructs the proper output for input polylines of size 1 or 0. 96 | // ASSUMES input is of size 0 or 1. 97 | inline Graph * trivial(std::vector * inputs); 98 | inline Graph * do_the_rest(); 99 | 100 | bool bUseFastAlgo; 101 | bool closed_loop; 102 | 103 | // -- Step 1. Compute canonical input structures. 104 | 105 | // The embedding is broken down into seperate phases. Here I have listed each operation, 106 | // followed by the data structures that they have built. 107 | 108 | // Appends the given input points to the collated single input point array. 109 | // Performs point fudging to avoid degenerate behavior. 110 | // Starts up the indexed collection of points. 111 | void loadInput(std::vector * inputs); 112 | 113 | // The canonical collection of points at their proper indices. 114 | std::vector points; 115 | // The original input lines. 116 | std::vector lines_initial; 117 | 118 | // -- Step 2. Find intersections in the input and compute the embedded polyline structure. 119 | 120 | // Intersects the input lines, then splits them and connects them appropiatly. 121 | // Populates the list of edge disjoint lines that only intersect at vertices. 122 | // puts the edge in consecutive order following the input polylines. 123 | // results put into this.lines_split 124 | void splitIntersectionPoints(); 125 | 126 | // Split version of original input lines, where lines only intersect at vertices. 127 | std::vector lines_split; 128 | 129 | // Allocates the output graph object and allocates vertices, edges, and halfedges for the input data. 130 | // Vertices are Indexed as follows [original points 1 for input polyline 1, then 2, ..., 131 | // new intersection points for polyline 1, then 2, etc, ...] 132 | // Halfedges are indexed in polyline input order, then in backwards input order. 133 | // -- Step 3. Proccess the embedded input and initialize the Planar Graph vertices, edges, and halfedges. 134 | void allocate_graph_from_input(); 135 | 136 | // The graph that is being built. 137 | // Once it is returned, the responsibility for this memory transfers to the user and the pointer is forgotten from this class. 138 | // FIXME: Shared_ptr or some other supposedly better pointer type? 139 | Graph * graph; 140 | 141 | // -- Step 4. Sort all outgoing edge lists for intersection vertices by the cartesian angle of the edges. 142 | void sort_outgoing_edges_by_angle(); 143 | 144 | // Step 4 helper function. 145 | // Sorts the outgoing_edges by the angles of the lines from the center 146 | //point to the points cooresponding to the outgoing edges. 147 | void sort_outgoing_edges(std::vector & outgoing_indices); 148 | 149 | // -- Step 5. 150 | // Determines the next and previous pointers for the halfedges in the Graph. 151 | // This is done almost entirely using the sets of outgoing edges for each vertex. 152 | // vertices of degree 2 associate their 2 pairs of neighbors. 153 | // vertices of degree are on a tail and associate their one pair of neighbors. 154 | // vertices of degree >2 are intersection points and they first sort their neighbors, then associate their star. 155 | // This function sets the Vertex_Data objects classification data. 156 | void associate_halfedge_cycles(); 157 | 158 | 159 | // Step 6. 160 | // Uses the vertex and edge complete halfedge mesh to add face data. 161 | // Also produces simpler cycle structures along that serve as an alternate representation of the faces. 162 | Graph * deriveFaces(); 163 | 164 | /* 165 | * REQUIRES: 1. face -> halfedge well defined already. 166 | * 2. halfedge next pointer well defined already. 167 | * ENSURES: links every halfedge in the loop starting and ending at face -> halfedge 168 | * with the face. 169 | */ 170 | void trace_face(Face * face); 171 | 172 | // Free all of the intermediary data structures. 173 | // Clear input structures. 174 | // Unmark the output. 175 | void cleanup(); 176 | 177 | 178 | // Helper functions. 179 | private: 180 | 181 | // Application Specific allocation functions. 182 | // REQUIRE: All allocation function need the graph to be already instantiated. 183 | 184 | Graph * newGraph() 185 | { 186 | Graph * output = new Graph(); 187 | output -> data = new Graph_Data(output); 188 | return output; 189 | } 190 | 191 | Face * newFace() 192 | { 193 | Face * output = graph -> newFace(); 194 | output -> data = new Face_Data(output); 195 | return output; 196 | }; 197 | 198 | Edge * newEdge() 199 | { 200 | Edge * output = graph -> newEdge(); 201 | output -> data = new Edge_Data(output); 202 | return output; 203 | } 204 | 205 | Halfedge * newHalfedge() 206 | { 207 | Halfedge * output = graph -> newHalfedge(); 208 | output -> data = new Halfedge_Data(output); 209 | return output; 210 | } 211 | 212 | Vertex * newVertex() 213 | { 214 | Vertex * output = graph -> newVertex(); 215 | output -> data = new Vertex_Data(output); 216 | return output; 217 | } 218 | }; 219 | } -------------------------------------------------------------------------------- /src/PolylineGraphMain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * One Stop Include File. 5 | * 6 | * Written by Bryce Summers on 8/18/2016 7 | * 8 | * This header loads all of the necessary files for working with Polyline Graph Embeddings. 9 | */ 10 | 11 | // Original Single Purpose Embedding and curve offsetting. 12 | #include "FaceFinder.h" 13 | #include "OffsetCurves.h" 14 | 15 | // General purpose Half edge mesh / graph representation, 16 | // which may be used to easily implement a variety of sophisticated operations. 17 | #include "HalfedgeGraph.h" 18 | 19 | // Application specific classes for working with embedded Polyline graphs. 20 | // Since we control the embedding in house, 21 | // we get to add as much secret sauce and useful invariants and extra information to the data 22 | // to suit our algorithmic and processing needs. 23 | #include "PolylineGraphEmbedder.h" 24 | #include "PolylineGraphData.h" 25 | #include "PolylineGraphPostProcessor.h" -------------------------------------------------------------------------------- /src/PolylineGraphPostProcessor.cpp: -------------------------------------------------------------------------------- 1 | #include "PolylineGraphPostProcessor.h" 2 | 3 | namespace scrib 4 | { 5 | 6 | /* 7 | PolylineGraphPostProcessor::PolylineGraphPostProcessor() 8 | { 9 | } 10 | 11 | 12 | PolylineGraphPostProcessor::~PolylineGraphPostProcessor() 13 | { 14 | } 15 | */ 16 | 17 | float computeAreaOfPolygon(Point_Vector_Format * closed_polygon) 18 | { 19 | int len = closed_polygon->size(); 20 | ofPoint * p1 = &(closed_polygon->at(len - 1).point); 21 | 22 | float area = 0.0; 23 | 24 | // Compute based on Green's Theorem. 25 | for (int i = 0; i < len; i++) 26 | { 27 | ofPoint * p2 = &(closed_polygon->at(i).point); 28 | area += (p2->x + p1->x)*(p2->y - p1->y); 29 | p1 = p2;// Shift p2 to p1. 30 | } 31 | 32 | return area / 2.0; 33 | } 34 | 35 | float computeAreaOfPolygon(OF_Point_Vector_Format * closed_polygon) 36 | { 37 | int len = closed_polygon->size(); 38 | ofPoint * p1 = &(closed_polygon->at(len - 1)); 39 | 40 | float area = 0.0; 41 | 42 | // Compute based on Green's Theorem. 43 | for (int i = 0; i < len; i++) 44 | { 45 | ofPoint * p2 = &(closed_polygon->at(i)); 46 | area += (p2->x + p1->x)*(p2->y - p1->y); 47 | p1 = p2;// Shift p2 to p1. 48 | } 49 | 50 | return area / 2.0; 51 | } 52 | 53 | bool isComplemented(Point_Vector_Format * closed_polygon) 54 | { 55 | return computeAreaOfPolygon(closed_polygon) > 0; 56 | } 57 | 58 | bool isComplemented(OF_Point_Vector_Format * closed_polygon) 59 | { 60 | return computeAreaOfPolygon(closed_polygon) > 0; 61 | } 62 | 63 | Face_Vector_Format * PolylineGraphPostProcessor::convert_to_face_vectors() 64 | { 65 | Face_Vector_Format * output = new Face_Vector_Format(); 66 | 67 | Face_Iter start = graph->facesBegin(); 68 | Face_Iter end = graph->facesEnd(); 69 | 70 | for (Face_Iter face = start; face != end; face++) 71 | { 72 | Point_Vector_Format * face_output = new Point_Vector_Format(); 73 | 74 | Halfedge * starting_half_edge = (*face) -> halfedge; 75 | Halfedge * current = starting_half_edge; 76 | 77 | // Convert the entire face into point info objects. 78 | do 79 | { 80 | Vertex * vert = current -> vertex; 81 | Vertex_Data * vert_data = vert -> data; 82 | 83 | ofPoint point = vert_data->point; 84 | int ID = vert -> ID; 85 | 86 | face_output -> push_back(point_info(point, ID, current)); 87 | 88 | // Iterate. 89 | current = current->next; 90 | } while (starting_half_edge != current); 91 | 92 | output -> push_back(face_output); 93 | } 94 | 95 | return output; 96 | } 97 | 98 | void PolylineGraphPostProcessor::determineComplementedFaces(std::vector * output) 99 | { 100 | Face_Vector_Format * input = face_vector; 101 | 102 | int len = input -> size(); 103 | 104 | for (int index = 0; index < len; index++) 105 | { 106 | float area = computeAreaOfPolygon(input -> at(index)); 107 | 108 | if (area > 0) 109 | { 110 | output -> push_back(index); 111 | } 112 | } 113 | } 114 | 115 | void PolylineGraphPostProcessor::determineNonTrivialAreaFaces(Int_Vector_Format * output, float min_area) 116 | { 117 | Face_Vector_Format * input = face_vector; 118 | 119 | int len = input -> size(); 120 | 121 | for (int index = 0; index < len; index++) 122 | { 123 | float area = computeAreaOfPolygon(input -> at(index)); 124 | 125 | // Absolute value to account for external faces. 126 | area = area >= 0 ? (area) : (-area); 127 | 128 | if (area >= min_area) 129 | { 130 | output -> push_back(index); 131 | } 132 | } 133 | } 134 | 135 | void PolylineGraphPostProcessor::determineTrivialAreaFaces(Int_Vector_Format * output, float min_area) 136 | { 137 | Face_Vector_Format * input = face_vector; 138 | 139 | int len = input -> size(); 140 | 141 | for (int index = 0; index < len; index++) 142 | { 143 | float area = computeAreaOfPolygon(input -> at(index)); 144 | 145 | // Absolute value to account for external faces. 146 | area = area >= 0 ? (area) : (-area); 147 | 148 | if (area < min_area) 149 | { 150 | output -> push_back(index); 151 | } 152 | } 153 | } 154 | 155 | Face_Vector_Format * PolylineGraphPostProcessor::clipTails() 156 | { 157 | Face_Vector_Format * input = face_vector; 158 | Face_Vector_Format * output = new Face_Vector_Format(); 159 | 160 | int len = input -> size(); 161 | 162 | for (int index = 0; index < len; index++) 163 | { 164 | Point_Vector_Format * unclipped_face = input -> at(index); 165 | Point_Vector_Format * clipped_face = clipTails(unclipped_face); 166 | 167 | // Append only non trivial faces to the output. 168 | if (clipped_face -> size() > 0) 169 | { 170 | output -> push_back(clipped_face); 171 | } 172 | } 173 | 174 | return output; 175 | } 176 | 177 | // Note: May return 0 point polygons if given trivial polygons. 178 | // < 3 point inputs --> 0 point outputs! 179 | Point_Vector_Format * PolylineGraphPostProcessor::clipTails(Point_Vector_Format * input) 180 | { 181 | Point_Vector_Format * output = new Point_Vector_Format(); 182 | 183 | int len = input -> size(); 184 | 185 | // Faces cannot enclose area if they have less than 3 vertices. 186 | // This pre check rules out trivial input without a start and end point. 187 | if (len < 3) 188 | { 189 | return output; // EMPTY. 190 | } 191 | 192 | int p_start = (input -> at(0)).ID; 193 | int p_end = (input -> at(len - 1)).ID; 194 | 195 | // A polygon is closed if it has identical starting and ending points. 196 | bool closed = (p_start == p_end); 197 | 198 | // For now, we will pretend that the duplicated point does not exist by pruning it. 199 | // Don't worry, we will add it back later. 200 | if (closed) 201 | { 202 | len -= 1; 203 | p_end = (input -> at(len - 1)).ID; 204 | } 205 | 206 | // We again remove input that cannot possibly enclose an area. 207 | // We do this again to rule out stuff like single line segments input in closed mode. 208 | if (len < 3) 209 | { 210 | return output; // EMPTY. 211 | } 212 | 213 | 214 | // The main idea behind tail clipping is to transform regions of the form ABA --> A, 215 | // in other words removing any consecutive pairs of half edges cooresponding to the same full edge. 216 | // We therefore 217 | 218 | // Stores whether the last iteration of the loop clipped a tail ending. 219 | // We can use this to ensure that the loop goes around the starting point to properly clip all tails. 220 | bool clipped_previous = false; 221 | bool non_empty_output = false; 222 | 223 | // NOTE: we will be using the len variable in this routine to gradually cull the back of the list as needed which 224 | // wraps around the arbitrary list starting point location. 225 | 226 | for (int i = 0; i < len; i++) 227 | { 228 | // Determine the nearest previous unpruned point, which will be pruned if it is mirrored by the next point. 229 | int p_previous; 230 | non_empty_output = output -> size() > 0; 231 | 232 | // A non pruned point exists in the output. 233 | if (non_empty_output) 234 | { 235 | p_previous = (output -> back()).ID; 236 | } 237 | else //Otherwise use the unpruned point at the tail of the unpruned list prefix sublist. 238 | { 239 | p_previous = (input -> at(len - 1)).ID; 240 | } 241 | 242 | int p_next = (input -> at((i + 1) % len)).ID; 243 | 244 | // If haven't locally detected a tail, then we simply push the point onto the output. 245 | if (p_previous != p_next) 246 | { 247 | output -> push_back(input -> at(i % len)); 248 | clipped_previous = false; 249 | continue; 250 | } 251 | 252 | // Actually prune the current point and the previous point. 253 | clipped_previous = true; 254 | 255 | if (non_empty_output) 256 | { 257 | // Prune output point. 258 | output -> pop_back(); 259 | } 260 | else 261 | { 262 | // Prune tail point. 263 | len -= 1; 264 | } 265 | 266 | // Don't add the current point, because we prune it as well. 267 | // If p_next ends up now being a non tail point, it will be successfully added during the next iteration. 268 | // We don't add it now, because we want to give it the opportunity to pruned by its next neighbor. 269 | continue; 270 | 271 | } 272 | 273 | // Due to the wrap around nature of face loops, the beginning may be in the middle of a tail, 274 | // so, if necessary, we now need to prune the beginning of the output 275 | 276 | // We will now prune the beginning of the output. 277 | int prune_num = 0; 278 | while (clipped_previous)// Essentially a while true loop if clipped_previous is true at the end of the first pass. 279 | { 280 | int len = output -> size(); 281 | 282 | // Entire face has been pruned. 283 | if (len < 3) 284 | { 285 | return output; 286 | } 287 | 288 | int p_previous = (output -> at(len - 1)).ID; 289 | int p_next = (output -> at(prune_num + 1)).ID; 290 | 291 | if (p_previous != p_next) 292 | { 293 | break; 294 | } 295 | 296 | // Prune the head and tail of the output. 297 | prune_num += 1; 298 | output -> pop_back(); 299 | continue; 300 | } 301 | 302 | // If the wrap pruning pass was done, we need to downsize the array to exclude the no longer relevant data. 303 | if (clipped_previous) 304 | { 305 | output -> erase(output -> begin(), output -> begin() + prune_num); 306 | } 307 | 308 | // Here we add the duplicate point if the input was closed. 309 | if (closed && non_empty_output) 310 | { 311 | point_info start = output -> at(0); 312 | output -> push_back(start); 313 | } 314 | 315 | return output; 316 | } 317 | 318 | 319 | 320 | std::vector * PolylineGraphPostProcessor::mergeFaces(ID_Set * face_ID_set) 321 | { 322 | // Temporarily store the raw list of face_info's. 323 | std::vector < face_info *> faces_uncomplemented; 324 | std::vector < face_info *> faces_complemented; 325 | 326 | // Go through all halfedges in all relevant faces and trace any representational union faces one time each. 327 | for (auto iter = face_ID_set -> begin(); iter != face_ID_set -> end(); iter++) 328 | { 329 | Face * face = graph -> getFace(*iter); 330 | Halfedge * start = face -> halfedge; 331 | Halfedge * current = face -> halfedge; 332 | do 333 | { 334 | if (current -> data -> marked == false && _halfedgeInUnion(face_ID_set, current)) 335 | { 336 | face_info * face = _traceUnionFace(face_ID_set, current); 337 | if (!isComplemented(&(face -> points))) 338 | { 339 | face -> complemented = false; 340 | faces_uncomplemented.push_back(face); 341 | } 342 | else 343 | { 344 | face -> complemented = true; 345 | faces_complemented.push_back(face); 346 | } 347 | } 348 | 349 | // Try the next edge. 350 | current = current -> next; 351 | } while (current != start); 352 | } 353 | 354 | // Clear markings. 355 | graph -> data -> clearHalfedgeMarks(); 356 | 357 | // Now we associate face_info objects with their internal complemented hole objects. 358 | std::vector * output = new std::vector(); 359 | 360 | std::map map; 361 | 362 | // On the first pass we add all exterior faces to the output and the map. 363 | for (auto iter = faces_uncomplemented.begin(); iter != faces_uncomplemented.end(); iter++) 364 | { 365 | face_info * face = *iter; 366 | 367 | output -> push_back(face); 368 | ID_Set & set = face -> faces_ID_set; 369 | for (auto id = set.begin(); id != set.end(); id++) 370 | { 371 | map[*id] = face; 372 | } 373 | 374 | } 375 | 376 | // On the second pass we add all complemented faces to the proper uncomplemented face hole set. 377 | // FIXME: Think about what will happen if their is a complemented face that should be by itself. 378 | // What about merging a complemented and uncomplemented face. 379 | for (auto iter = faces_complemented.begin(); iter != faces_complemented.end(); iter++) 380 | { 381 | face_info * face = *iter; 382 | int ID = *(face -> faces_ID_set.begin()); 383 | std::map::const_iterator it = map.find(ID); 384 | 385 | // If the index is not associated with an uncomplemented face, then this uncomplemented face must be singleton, 386 | // instead of a hole. We therefore add it to the direct output. 387 | if (it == map.end()) 388 | { 389 | output -> push_back(face); 390 | continue; 391 | } 392 | 393 | // Otherwise we add it as a hole to the relevant face. 394 | face_info * uncomplemented_face = it -> second; 395 | uncomplemented_face -> holes.push_back(face); 396 | } 397 | 398 | return output; 399 | } 400 | 401 | bool PolylineGraphPostProcessor::_halfedgeInUnion(ID_Set * face_ID_set, Halfedge * start) 402 | { 403 | Face * face = start -> face; 404 | int face_ID = face -> ID; 405 | ID_Set::const_iterator face_iter = face_ID_set -> find(face_ID); 406 | 407 | Face * twin_face = start -> twin -> face; 408 | int twin_ID = twin_face -> ID; 409 | ID_Set::const_iterator twin_iter = face_ID_set -> find(twin_ID); 410 | 411 | // true iff Twin face not in the set of faces in the union. 412 | auto NOT_FOUND = face_ID_set -> end(); 413 | 414 | // NOTE: Tail edges with the same Face on both sides are treated as not in the union, 415 | 416 | return face_iter != NOT_FOUND && twin_iter == NOT_FOUND; 417 | } 418 | 419 | face_info * PolylineGraphPostProcessor::_traceUnionFace(ID_Set * face_ID_set, Halfedge * start) 420 | { 421 | face_info * output = new face_info(); 422 | ID_Set & output_ID_set = output -> faces_ID_set; 423 | 424 | // We only need to worry about tracing output points, 425 | // because the holes will associated later on from faces tracede with this function. 426 | Point_Vector_Format & output_points = output -> points; 427 | 428 | Halfedge * current = start; 429 | do 430 | { 431 | current -> data -> marked = true; 432 | 433 | // Output current point (with halfedge). 434 | point_info current_point = halfedgeToPointInfo(current); 435 | output_points.push_back(current_point); 436 | int current_face_ID = current -> face -> ID; 437 | output_ID_set.insert(current_face_ID); 438 | 439 | // Transition to the next halfedge along this union face. 440 | current = nextUnionFace(face_ID_set, current); 441 | 442 | } while (current != start); 443 | 444 | return output; 445 | } 446 | 447 | Halfedge * PolylineGraphPostProcessor::nextUnionFace(ID_Set * face_ID_Set, Halfedge * current) 448 | { 449 | // Go around the star backwards. 450 | 451 | // Transition from the incoming current edge to the backmost candidate outgoing edge. 452 | current = current -> twin -> prev -> twin; 453 | 454 | // NOTE: WE could theoretically put in an infinite loop check here, because this code will fail if the graph is malformed. 455 | 456 | // Keep trying out candidate outgoing faces, until we find the first one that works. 457 | while(!_halfedgeInUnion(face_ID_Set, current)) 458 | { 459 | // The cycling operations come in two forms, since we flip our orientation after each path change attempt. 460 | current = current -> prev -> twin; 461 | } 462 | 463 | return current; 464 | } 465 | 466 | point_info PolylineGraphPostProcessor::halfedgeToPointInfo(Halfedge * halfedge) 467 | { 468 | Vertex * vertex = halfedge -> vertex; 469 | Vertex_Data * vertex_data = vertex -> data; 470 | return point_info(vertex_data -> point, vertex -> ID, halfedge); 471 | } 472 | } -------------------------------------------------------------------------------- /src/PolylineGraphPostProcessor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | #include "HalfedgeGraph.h" 5 | #include "PolylineGraphData.h" 6 | 7 | /* The PolylineGraphPostProcessor class. 8 | * 9 | * Written and adapted from FaceFinder on 8/18/2016. 10 | * 11 | * Purpose: 12 | * 13 | * Allows users to convert Planar Polyline Embedded Graphs into mainstream C++ data structures. 14 | * This class then provides some useful processing algorithms on these output structures. 15 | * 16 | * I may also put information extraction algorithms here. 17 | * 18 | * The key idea is that the post processor doesn't mutate the Graph object in any way. 19 | * FIXME: Use the Graph Mutator Proccessor instead (Currenly non existant.) if you wish to modify the graph. 20 | * 21 | * Maybe I will make a modification mode. 22 | */ 23 | 24 | namespace scrib 25 | { 26 | 27 | // Forward declare types. 28 | class point_info; 29 | class face_info; 30 | 31 | 32 | // Convenience Typedefs. 33 | // Same old fashion mainstream c++ vectors, just an alis name. 34 | typedef std::vector Point_Vector_Format; 35 | typedef std::vector OF_Point_Vector_Format; 36 | typedef std::vector Face_Vector_Format; 37 | typedef std::vector Int_Vector_Format; 38 | typedef std::set ID_Set; 39 | 40 | class point_info 41 | { 42 | public: 43 | 44 | // -- Constructor. 45 | 46 | // Used in Facefinder for non halfedgemesh based embeddings. 47 | point_info(ofPoint p, int id) 48 | { 49 | point = p; 50 | ID = id; 51 | } 52 | 53 | point_info(ofPoint p, int id, Halfedge * hedge) 54 | { 55 | point = p; 56 | ID = id; 57 | this -> halfedge = hedge; 58 | } 59 | 60 | ofPoint point; 61 | int ID; 62 | 63 | // The halfedge that this point represents when this point is collected in a Point_Vector to represent a face. 64 | // This may be used to easily extract local connectivity information and attributes for this point and its neighbors. 65 | // WARNING: This always points to the original embedding's connectivity information, 66 | // which means that things like next pointers may no longer be valid after tails are clipped or other algorithms. 67 | // Faces and twin pointers should still be valid though... 68 | // Undefined for output from scrib::FaceFinder. 69 | Halfedge * halfedge = NULL; 70 | }; 71 | 72 | class face_info 73 | { 74 | public: 75 | //int color; 76 | std::vector holes; 77 | Point_Vector_Format points; 78 | 79 | // Contains a list of all faces contributing to this unioned face. 80 | ID_Set faces_ID_set; 81 | 82 | bool complemented; 83 | }; 84 | 85 | /* 86 | * http://math.blogoverflow.com/2014/06/04/greens-theorem-and-area-of-polygons/ 87 | * Computes the area of a 2D polygon directly from the polygon's coordinates. 88 | * The area will be positive or negative depending on the 89 | * clockwise / counter clockwise orientation of the points. 90 | * Also see: https://brycesummers.wordpress.com/2015/08/24/a-proof-of-simple-polygonal-area-via-greens-theorem/ 91 | */ 92 | float computeAreaOfPolygon(Point_Vector_Format * closed_polygon); 93 | bool isComplemented (Point_Vector_Format * closed_polygon); 94 | 95 | /* 96 | * http://math.blogoverflow.com/2014/06/04/greens-theorem-and-area-of-polygons/ 97 | * Computes the area of a 2D polygon directly from the polygon's coordinates. 98 | * The area will be positive or negative depending on the 99 | * clockwise / counter clockwise orientation of the points. 100 | * Also see: https://brycesummers.wordpress.com/2015/08/24/a-proof-of-simple-polygonal-area-via-greens-theorem/ 101 | */ 102 | float computeAreaOfPolygon(OF_Point_Vector_Format * closed_polygon); 103 | bool isComplemented (OF_Point_Vector_Format * closed_polygon); 104 | 105 | class PolylineGraphPostProcessor 106 | { 107 | 108 | public: 109 | PolylineGraphPostProcessor() {} 110 | virtual ~PolylineGraphPostProcessor() {} 111 | 112 | private: 113 | // Data. 114 | Graph * graph = NULL; 115 | 116 | // Face point vector format. // FIXME: Consider using smart pointers. 117 | Face_Vector_Format * face_vector = NULL; 118 | 119 | public: 120 | 121 | // -- Data Structure Conversion. 122 | Face_Vector_Format * convert_to_face_vectors(); 123 | 124 | 125 | // -- This class performs operations on face vectors, but it only uses the current face vector as an input. 126 | // The class never changes the loaded face vector internally. 127 | // It is up to the user to load the proper face vector when they need a change. 128 | void load_face_vector(Face_Vector_Format * face_vector_format) 129 | { 130 | this -> face_vector = face_vector_format; 131 | } 132 | 133 | void free_face_vector() 134 | { 135 | if (this -> face_vector != NULL) 136 | { 137 | delete this -> face_vector; 138 | this -> face_vector = NULL; 139 | } 140 | } 141 | 142 | void load_graph(Graph * graph) 143 | { 144 | this -> graph = graph; 145 | } 146 | 147 | void freeGraph() 148 | { 149 | if(this -> graph != NULL) 150 | { 151 | delete this -> graph; 152 | this -> graph = NULL; 153 | } 154 | } 155 | 156 | 157 | // -- Post processing algorithms. 158 | 159 | // Appends the indices of any external faces amongst the input list of faces to the output vector. 160 | // NOTE : The input type is equivelant to the output type of the face finding functions, 161 | // so using this function may be a natural extension of using the original functions. 162 | void determineComplementedFaces(std::vector * output); 163 | 164 | // Appends to output the indices of the faces of **NonTrivial** Area (area >= min_area) 165 | void determineNonTrivialAreaFaces(Int_Vector_Format * output, float min_area); 166 | // Appends to output the indices of the faces of **Trivial** Area (area < min_area) 167 | void determineTrivialAreaFaces(Int_Vector_Format * output, float min_area); 168 | 169 | // Input: a set of faces, Output: a new set of faces that have no trivial contiguous subfaces. 170 | // ENSURES: Polygons will be output either open or closed in the manner that they are passed in. 171 | // ENSURES: Omits faces consisting of only a single long tail. 172 | // The user is still responsible to deallocating the original vector. 173 | Face_Vector_Format * clipTails(); 174 | 175 | // Returns a copy of the single input face without any trivial area contiguous subfaces. (Tails) 176 | // May return a 0 point polyline if the input line is non-intersecting. 177 | Point_Vector_Format * clipTails(Point_Vector_Format * input); 178 | 179 | // Uses the currently loaded this->graph object as Input. 180 | // Takes in a vector containing the integer IDs of the faces to be merged. 181 | // Outputs the result of unioning all of the faces. 182 | std::vector * mergeFaces(ID_Set * face_ID_set); 183 | 184 | private: 185 | 186 | // Returns true iff the given hafedge is included in the output of the union of the given faces. 187 | // I.E. returns true iff the given half edge -> face is within the set of unioned faces and half_edge->twin -> face is not. 188 | // Tail edges, where the halfedge and its twin are on the same face are not considered to be in a halfedgeUnion face. 189 | bool _halfedgeInUnion(ID_Set * face_ID_set, Halfedge * start); 190 | 191 | // Given an In Union halfege, traces its face_info union face information. 192 | // Properly sets the output's: points and face_IDs fields. 193 | // Marks halfedges, therefore calling functions are responsible for unmarking halfedges. 194 | face_info * _traceUnionFace(ID_Set * face_ID_set, Halfedge * start); 195 | 196 | // Given a halfedge inside of a unionface, returns the next halfedge within that face. 197 | Halfedge * nextUnionFace(ID_Set * face_ID_Set, Halfedge * current); 198 | 199 | point_info halfedgeToPointInfo(Halfedge * halfedge); 200 | 201 | }; 202 | } --------------------------------------------------------------------------------