diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2016-04-15 17:52:35 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2016-04-15 17:52:35 -0400 |
commit | 9d021d0ef0e24595cd35e42d2847a6e679c386e0 (patch) | |
tree | 0cedf25e9ac9f95399d72e27aff75c625ae83b0e | |
download | difference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.tar.gz difference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.tar.bz2 difference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.zip |
Initial commit
-rw-r--r-- | .gitmodules | 9 | ||||
-rw-r--r-- | CMakeLists.txt | 18 | ||||
-rwxr-xr-x | coolvetica.ttf | bin | 0 -> 135916 bytes | |||
-rw-r--r-- | difference.cpp | 354 | ||||
m--------- | vendor/curlcpp | 0 | ||||
m--------- | vendor/libtwittercpp | 0 | ||||
m--------- | vendor/verbly | 0 |
7 files changed, 381 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6c14770 --- /dev/null +++ b/.gitmodules | |||
@@ -0,0 +1,9 @@ | |||
1 | [submodule "vendor/verbly"] | ||
2 | path = vendor/verbly | ||
3 | url = https://github.com/hatkirby/verbly | ||
4 | [submodule "vendor/libtwittercpp"] | ||
5 | path = vendor/libtwittercpp | ||
6 | url = https://github.com/hatkirby/libtwittercpp | ||
7 | [submodule "vendor/curlcpp"] | ||
8 | path = vendor/curlcpp | ||
9 | url = https://github.com/JosephP91/curlcpp | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..da4306f --- /dev/null +++ b/CMakeLists.txt | |||
@@ -0,0 +1,18 @@ | |||
1 | cmake_minimum_required (VERSION 3.1) | ||
2 | project (difference) | ||
3 | |||
4 | set(CMAKE_BUILD_TYPE Debug) | ||
5 | |||
6 | add_subdirectory(vendor/libtwittercpp) | ||
7 | add_subdirectory(vendor/verbly) | ||
8 | |||
9 | find_package(PkgConfig) | ||
10 | pkg_check_modules(YamlCpp yaml-cpp REQUIRED) | ||
11 | pkg_check_modules(sqlite3 sqlite3 REQUIRED) | ||
12 | pkg_check_modules(GraphicsMagick GraphicsMagick++ REQUIRED) | ||
13 | |||
14 | include_directories(vendor/libtwittercpp/src ${sqlite3_INCLUDE_DIR} vendor/verbly/lib vendor/curlcpp/include ${GraphicsMagick_INCLUDE_DIRS} vendor/libtwittercpp/vendor/json/src) | ||
15 | add_executable(difference difference.cpp) | ||
16 | set_property(TARGET difference PROPERTY CXX_STANDARD 11) | ||
17 | set_property(TARGET difference PROPERTY CXX_STANDARD_REQUIRED ON) | ||
18 | target_link_libraries(difference verbly ${sqlite3_LIBRARIES} ${YamlCpp_LIBRARIES} twitter++ curlcpp ${GraphicsMagick_LIBRARIES}) | ||
diff --git a/coolvetica.ttf b/coolvetica.ttf new file mode 100755 index 0000000..410ca31 --- /dev/null +++ b/coolvetica.ttf | |||
Binary files differ | |||
diff --git a/difference.cpp b/difference.cpp new file mode 100644 index 0000000..66e4550 --- /dev/null +++ b/difference.cpp | |||
@@ -0,0 +1,354 @@ | |||
1 | #include <verbly.h> | ||
2 | #include <twitter.h> | ||
3 | #include <curl_easy.h> | ||
4 | #include <list> | ||
5 | #include <set> | ||
6 | #include <vector> | ||
7 | #include <algorithm> | ||
8 | #include <cstdlib> | ||
9 | #include <ctime> | ||
10 | #include <Magick++.h> | ||
11 | #include <iostream> | ||
12 | #include <yaml-cpp/yaml.h> | ||
13 | #include <unistd.h> | ||
14 | |||
15 | std::string capitalize(std::string input) | ||
16 | { | ||
17 | std::string result; | ||
18 | bool capnext = true; | ||
19 | |||
20 | for (auto ch : input) | ||
21 | { | ||
22 | if (capnext) | ||
23 | { | ||
24 | result += toupper(ch); | ||
25 | capnext = false; | ||
26 | } else { | ||
27 | result += ch; | ||
28 | } | ||
29 | |||
30 | if ((ch == ' ') || (ch == '-')) | ||
31 | { | ||
32 | capnext = true; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | return result; | ||
37 | } | ||
38 | |||
39 | int main(int argc, char** argv) | ||
40 | { | ||
41 | srand(time(NULL)); | ||
42 | Magick::InitializeMagick(nullptr); | ||
43 | |||
44 | int delay = 60 * 60; | ||
45 | |||
46 | YAML::Node config = YAML::LoadFile("config.yml"); | ||
47 | twitter::auth auth; | ||
48 | auth.setConsumerKey(config["consumer_key"].as<std::string>()); | ||
49 | auth.setConsumerSecret(config["consumer_secret"].as<std::string>()); | ||
50 | auth.setAccessKey(config["access_key"].as<std::string>()); | ||
51 | auth.setAccessSecret(config["access_secret"].as<std::string>()); | ||
52 | |||
53 | twitter::client client(auth); | ||
54 | |||
55 | verbly::data database("data.sqlite3"); | ||
56 | |||
57 | auto whitelist = database.nouns(); | ||
58 | whitelist.with_wnid(111530512); // Crops (plants) | ||
59 | whitelist.with_wnid(111536087); // Ornamental (plants) | ||
60 | whitelist.with_wnid(111536230); // Pot plants | ||
61 | whitelist.with_wnid(113083023); // Houseplants | ||
62 | whitelist.with_wnid(113083306); // Garden plants | ||
63 | whitelist.with_wnid(113083586); // Vascular plants (includes flowers) | ||
64 | whitelist.with_wnid(109287968); // Geological formations | ||
65 | whitelist.with_wnid(109208496); // Asterisms (collections of stars) | ||
66 | whitelist.with_wnid(109239740); // Celestial bodies | ||
67 | whitelist.with_wnid(109277686); // Exterrestrial objects (comets and meteroids) | ||
68 | whitelist.with_wnid(109403211); // Radiators (supposedly natural radiators but actually these are just pictures of radiators) | ||
69 | whitelist.with_wnid(109416076); // Rocks | ||
70 | whitelist.with_wnid(105442131); // Chromosomes | ||
71 | whitelist.with_wnid(100324978); // Tightrope walking | ||
72 | whitelist.with_wnid(100326094); // Rock climbing | ||
73 | whitelist.with_wnid(100433458); // Contact sports | ||
74 | whitelist.with_wnid(100433802); // Gymnastics | ||
75 | whitelist.with_wnid(100439826); // Track and field | ||
76 | whitelist.with_wnid(100440747); // Skiing | ||
77 | whitelist.with_wnid(100441824); // Water sport | ||
78 | whitelist.with_wnid(100445351); // Rowing | ||
79 | whitelist.with_wnid(100446980); // Archery | ||
80 | // TODO: add more sports | ||
81 | whitelist.with_wnid(100021939); // Artifacts | ||
82 | whitelist.with_wnid(101471682); // Vertebrates | ||
83 | |||
84 | auto whitedata = whitelist.run(); | ||
85 | std::set<int> wnids; | ||
86 | verbly::filter<verbly::noun> whitefilter; | ||
87 | whitefilter.set_orlogic(true); | ||
88 | for (auto wd : whitedata) | ||
89 | { | ||
90 | if (wnids.count(wd.wnid()) == 0) | ||
91 | { | ||
92 | whitefilter << wd; | ||
93 | wnids.insert(wd.wnid()); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | std::cout << "Started!" << std::endl; | ||
98 | for (;;) | ||
99 | { | ||
100 | std::cout << "Generating noun..." << std::endl; | ||
101 | verbly::noun pictured = database.nouns().full_hyponym_of(whitefilter).at_least_n_images(2).random().limit(1).run().front(); | ||
102 | std::cout << "Noun: " << pictured.singular_form() << std::endl; | ||
103 | std::cout << "Getting URLs..." << std::endl; | ||
104 | std::ostringstream lstbuf; | ||
105 | curl::curl_ios<std::ostringstream> lstios(lstbuf); | ||
106 | curl::curl_easy lsthandle(lstios); | ||
107 | std::string lsturl = pictured.imagenet_url(); | ||
108 | lsthandle.add<CURLOPT_URL>(lsturl.c_str()); | ||
109 | try { | ||
110 | lsthandle.perform(); | ||
111 | } catch (curl::curl_easy_exception error) { | ||
112 | error.print_traceback(); | ||
113 | sleep(delay); | ||
114 | |||
115 | continue; | ||
116 | } | ||
117 | |||
118 | if (lsthandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) | ||
119 | { | ||
120 | std::cout << "Could not get URLs" << std::endl; | ||
121 | continue; | ||
122 | } | ||
123 | |||
124 | std::cout << "Got URLs." << std::endl; | ||
125 | std::string lstdata = lstbuf.str(); | ||
126 | auto lstlist = verbly::split<std::list<std::string>>(lstdata, "\r\n"); | ||
127 | std::set<std::string> lstset; | ||
128 | for (auto url : lstlist) | ||
129 | { | ||
130 | lstset.insert(url); | ||
131 | } | ||
132 | |||
133 | if (lstset.size() < 2) | ||
134 | { | ||
135 | continue; | ||
136 | } | ||
137 | |||
138 | std::vector<std::string> lstvec; | ||
139 | for (auto url : lstset) | ||
140 | { | ||
141 | lstvec.push_back(url); | ||
142 | } | ||
143 | |||
144 | std::random_shuffle(std::begin(lstvec), std::end(lstvec)); | ||
145 | |||
146 | Magick::Blob img1; | ||
147 | Magick::Image pic1; | ||
148 | bool success = false; | ||
149 | int curind = 0; | ||
150 | for (; curind < lstvec.size(); curind++) | ||
151 | { | ||
152 | std::ostringstream img1buf; | ||
153 | curl::curl_ios<std::ostringstream> img1ios(img1buf); | ||
154 | curl::curl_easy img1handle(img1ios); | ||
155 | std::string img1url = lstvec[curind]; | ||
156 | img1handle.add<CURLOPT_URL>(img1url.c_str()); | ||
157 | img1handle.add<CURLOPT_CONNECTTIMEOUT>(30); | ||
158 | |||
159 | try { | ||
160 | img1handle.perform(); | ||
161 | } catch (curl::curl_easy_exception error) { | ||
162 | error.print_traceback(); | ||
163 | |||
164 | continue; | ||
165 | } | ||
166 | |||
167 | if (img1handle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) | ||
168 | { | ||
169 | continue; | ||
170 | } | ||
171 | |||
172 | if (std::string(img1handle.get_info<CURLINFO_CONTENT_TYPE>().get()).substr(0, 6) != "image/") | ||
173 | { | ||
174 | continue; | ||
175 | } | ||
176 | |||
177 | std::string img1str = img1buf.str(); | ||
178 | img1 = Magick::Blob(img1str.c_str(), img1str.length()); | ||
179 | pic1.read(img1); | ||
180 | if (pic1.rows() == 0) | ||
181 | { | ||
182 | continue; | ||
183 | } | ||
184 | |||
185 | std::cout << img1url << std::endl; | ||
186 | success = true; | ||
187 | |||
188 | break; | ||
189 | } | ||
190 | |||
191 | if (!success) | ||
192 | { | ||
193 | continue; | ||
194 | } | ||
195 | |||
196 | success = false; | ||
197 | Magick::Blob img2; | ||
198 | Magick::Image pic2; | ||
199 | for (curind++; curind < lstvec.size(); curind++) | ||
200 | { | ||
201 | std::ostringstream img2buf; | ||
202 | curl::curl_ios<std::ostringstream> img2ios(img2buf); | ||
203 | curl::curl_easy img2handle(img2ios); | ||
204 | std::string img2url = lstvec[curind]; | ||
205 | img2handle.add<CURLOPT_URL>(img2url.c_str()); | ||
206 | img2handle.add<CURLOPT_CONNECTTIMEOUT>(30); | ||
207 | |||
208 | try { | ||
209 | img2handle.perform(); | ||
210 | } catch (curl::curl_easy_exception error) { | ||
211 | error.print_traceback(); | ||
212 | |||
213 | continue; | ||
214 | } | ||
215 | |||
216 | if (img2handle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) | ||
217 | { | ||
218 | continue; | ||
219 | } | ||
220 | |||
221 | if (std::string(img2handle.get_info<CURLINFO_CONTENT_TYPE>().get()).substr(0, 6) != "image/") | ||
222 | { | ||
223 | continue; | ||
224 | } | ||
225 | |||
226 | std::string img2str = img2buf.str(); | ||
227 | img2 = Magick::Blob(img2str.c_str(), img2str.length()); | ||
228 | pic2.read(img2); | ||
229 | if (pic2.rows() == 0) | ||
230 | { | ||
231 | continue; | ||
232 | } | ||
233 | |||
234 | std::cout << img2url << std::endl; | ||
235 | success = true; | ||
236 | |||
237 | break; | ||
238 | } | ||
239 | |||
240 | if (!success) | ||
241 | { | ||
242 | continue; | ||
243 | } | ||
244 | |||
245 | std::string ant1, ant2; | ||
246 | if (rand()%2==0) | ||
247 | { | ||
248 | verbly::noun n1 = database.nouns().has_antonyms().random().limit(1).run().front(); | ||
249 | verbly::noun n2 = n1.antonyms().random().limit(1).run().front(); | ||
250 | ant1 = n1.singular_form(); | ||
251 | ant2 = n2.singular_form(); | ||
252 | } else { | ||
253 | verbly::adjective a1 = database.adjectives().has_antonyms().random().limit(1).run().front(); | ||
254 | verbly::adjective a2 = a1.antonyms().random().limit(1).run().front(); | ||
255 | ant1 = a1.base_form(); | ||
256 | ant2 = a2.base_form(); | ||
257 | } | ||
258 | |||
259 | if (pic1.columns() < 320) | ||
260 | { | ||
261 | pic1.zoom(Magick::Geometry(320, pic1.rows() * 320 / pic1.columns(), 0, 0)); | ||
262 | } | ||
263 | |||
264 | if (pic2.columns() < 320) | ||
265 | { | ||
266 | pic2.zoom(Magick::Geometry(320, pic2.rows() * 320 / pic2.columns(), 0, 0)); | ||
267 | } | ||
268 | |||
269 | int width = std::min(pic1.columns(), pic2.columns()); | ||
270 | int height = std::min(pic1.rows(), pic2.rows()); | ||
271 | Magick::Geometry geo1(width, height, pic1.columns()/2 - width/2, pic1.rows()/2 - height/2); | ||
272 | Magick::Geometry geo2(width, height, pic2.columns()/2 - width/2, pic2.rows()/2 - height/2); | ||
273 | pic1.crop(geo1); | ||
274 | pic2.crop(geo2); | ||
275 | |||
276 | Magick::Image composite(Magick::Geometry(width*2, height, 0, 0), "white"); | ||
277 | composite.draw(Magick::DrawableCompositeImage(0, 0, pic1)); | ||
278 | composite.draw(Magick::DrawableCompositeImage(width, 0, pic2)); | ||
279 | composite.font("@coolvetica.ttf"); | ||
280 | |||
281 | double fontsize = rand() % 72 + 16; | ||
282 | for (;;) | ||
283 | { | ||
284 | composite.fontPointsize(fontsize); | ||
285 | |||
286 | Magick::TypeMetric metric; | ||
287 | composite.fontTypeMetrics(ant1, &metric); | ||
288 | if (metric.textWidth() > 300) | ||
289 | { | ||
290 | fontsize -= 0.5; | ||
291 | continue; | ||
292 | } | ||
293 | |||
294 | composite.fontTypeMetrics(ant2, &metric); | ||
295 | if (metric.textWidth() > 300) | ||
296 | { | ||
297 | fontsize -= 0.5; | ||
298 | continue; | ||
299 | } | ||
300 | |||
301 | break; | ||
302 | } | ||
303 | |||
304 | Magick::TypeMetric metric; | ||
305 | composite.fontTypeMetrics(ant1, &metric); | ||
306 | int y = rand() % ((int)(composite.rows() - 40 - metric.textHeight())) + 20; | ||
307 | y = composite.rows() - y; | ||
308 | int x1 = (width - 40 - metric.textWidth())/2+20; | ||
309 | composite.fontTypeMetrics(ant2, &metric); | ||
310 | int x2 = (width - 40 - metric.textWidth())/2+20+width; | ||
311 | composite.strokeColor("white"); | ||
312 | composite.strokeWidth(1); | ||
313 | composite.draw(Magick::DrawableText(x1, y, ant1)); | ||
314 | composite.draw(Magick::DrawableText(x2, y, ant2)); | ||
315 | |||
316 | composite.magick("png"); | ||
317 | |||
318 | Magick::Blob outputimg; | ||
319 | composite.write(&outputimg); | ||
320 | |||
321 | std::cout << "Generated image!" << std::endl << "Tweeting..." << std::endl; | ||
322 | |||
323 | std::stringstream msg; | ||
324 | msg << capitalize(ant1); | ||
325 | msg << " vs. "; | ||
326 | msg << capitalize(ant2); | ||
327 | |||
328 | long media_id; | ||
329 | twitter::response resp = client.uploadMedia("image/png", (const char*) outputimg.data(), outputimg.length(), media_id); | ||
330 | if (resp != twitter::response::ok) | ||
331 | { | ||
332 | std::cout << "Twitter error while uploading image: " << resp << std::endl; | ||
333 | sleep(delay); | ||
334 | |||
335 | continue; | ||
336 | } | ||
337 | |||
338 | twitter::tweet tw; | ||
339 | resp = client.updateStatus(msg.str(), tw, {media_id}); | ||
340 | if (resp != twitter::response::ok) | ||
341 | { | ||
342 | std::cout << "Twitter error while tweeting: " << resp << std::endl; | ||
343 | sleep(delay); | ||
344 | |||
345 | continue; | ||
346 | } | ||
347 | |||
348 | std::cout << "Done!" << std::endl << "Waiting..." << std::endl << std::endl; | ||
349 | |||
350 | sleep(delay); | ||
351 | } | ||
352 | |||
353 | return 0; | ||
354 | } | ||
diff --git a/vendor/curlcpp b/vendor/curlcpp new file mode 160000 | |||
Subproject 0cdfb670d726252bdce4fb2a194e11de38a42e0 | |||
diff --git a/vendor/libtwittercpp b/vendor/libtwittercpp new file mode 160000 | |||
Subproject 7e85b35d7d1714e3f85434b891a1050ad584e33 | |||
diff --git a/vendor/verbly b/vendor/verbly new file mode 160000 | |||
Subproject 040ee58fecdc9c478004bc2e554e1ae126ec460 | |||