Category: Book

Feedback on Books

Feedback on Generative Books

For Assignment #6, “Generative Book”, students were given two weeks to write code to generate a book. The students were required to generate the content of the book, as well as to execute or generate its layout automatically through code. Lastly, students were expected to present their book in a printed, bound format. The reviewers for this project were Nick Montfort, Katie Rose Pipkin, Kyuha Shim, and Golan Levin (the professor of this course). Feedback from these reviewers is compiled below.

Nick Montfort develops computational art and poetry, often collaboratively. Montfort has helped to establish several new academic fields and approaches: Platform studies, critical code studies, and electronic literature. Montfort is a professor of digital literature at MIT, where he directs The Trope Tank, a DIY and boundary-transgressing research lab that undertakes scholarly and aesthetic projects and offers material computing resources.

Katie Rose Pipkin is a curator and multidisciplinary artist native to Austin, Texas. Pipkin works in language, with code and on paper. Pipkin makes drawings, as well as generative books and other computational artworks. Best known for creating Twitterbots, as well as generative poetry and other software, including games, Pipkin is presently a 2nd-year MFA student in the CMU School of Art.

Kyuha (Q) Shim is a designer, researcher and Assistant Professor in the School of Design at Carnegie Mellon University. Prior to coming to CMU, he held faculty positions at the Rhode Island School of Design and the Royal College of Art. He has worked as a researcher at the Jan van Eyck Academie and as a research fellow (Data Visualization Specialist) at MIT’s SENSEable City Laboratory.

Golan Levin, the director of this course, is an Associate Professor of Electronic Art at CMU, with courtesy appointments in Design, Architecture, Entertainment Technology, and Computer Science.


aliot

Aliot-Book

aliot-book

Nick Montfort: The word/image connection is good in this project. It seems a stretch to call this a computer-generated book, even if code was used to put it together. But since it was, and a book was made, for this assignment it seems all right. The book includes some photos and corresponding texts that were gathered, and could have been done in the 1960s with Polaroid photos as “What I Was Doing When You Called.” Also see long-term photo projects by Teching Hsieh and Alberto Frigo. The book is about the everyday and the ordinary, but does it provide any insight into that, even for the author, who finds the results “unsurprising” and does not mention learning anything?
Katie Rose Pipkin: Congrats- I think this is a successful project. The conceit and the scope are just right; it feels personal without being too navel-gazy to become uninteresting. I think if you can continue working in similarly clever ways (with small personal datasets, manageable scope, etc) you’ll be very successful in generative projects.
Kyuha Shim: Nice remix of text messages, images, emails and screen captures of social media app. Unfortunately, the repetitive layout could get boring.
Golan Levin: I can’t decide if this is conceptually profound, or profoundly self-absorbed. Maybe it’s a bit of both. That’s not a bad thing: it’s okay to make a book just for yourself. The algorithmic work is assuredly quite competent. One complaint: The graphic design feels arbitrary and unconsidered, and could have SO much more strongly supported the book’s concept. For example, if the book had used a vertical format (rather than landscape), you could have adopted the vertical aspect ratio of the cellphone selfies, while also eliminating those painful oceans of white paper flanking your images. I.e., make the book shaped like a cellphone.


anson

Anson-Book

anson-book

Nick Montfort: Although I’m not convinced by how danceable or dance-related these diagrams are, it’s overall quite effective to pair generated dance with combinatorial names, and some cultural and artistic background is brought into the project effectively. The design of the book is not very inspired. Why this number of dances and not more or less? Why no folios (page numbers) or running heads? Some of the numerals are obscured by arrow heads in the layout of diagrams. All your dances (like Warhol’s) are for one person?
Katie Rose Pipkin: This is a cute idea, and it is clear that you went all out on implementation. I’m happy to see generative work meant to be applied to the world at large, and hope you might consider conscripting some friends to attempt to perform some of these as part of your documentation. I think that the interaction between human systems (like dance) and machine systems (like eternal procedural generation) is where the field gets interesting. Just a note- when I came by to look at the books, I was under-impressed with the print job; unstapled 8.5×11 paper does not really make a book. Next time consider even doing something simple, like a stitch binding! The work will feel stronger for it.
Kyuha Shim: Interesting concept, but it is not clear how the ‘animal outlined foot patterns’ were generated. Reducing the size of text and bottom margin of your master spread would improve the overall composition.
Golan Levin: Intriguing and pleasing — a unique concept, a charming idea. The conceot has enough depth that one could spend a week developing it, or a whole year. Technically, this project was a nice microcosm of different technical challenges. I feel you would benefit from more practice solving the sorts of problems you encountered here.


antar

Antar-book

antar-book

Nick Montfort: The book concept and production is great. The “tease” of the inside page images might be even more effective if there were text to read more typically, rather than “wallpaper,” on the other side. But if you are going to Twitter to look for the way people use language there, why “clean” the tweets? “I still wanted my content to be sophisticated language” is not an answer, any more than “I wanted to give you a very low grade” justifies that. Why? Why edit the tweets you are appropriating from other people? It seems okay to include “IM” for “I’M,” so what makes the language wrong or in need of correction. It could be justified to remove “okay babe” since you have that as the title, but if you make other changes, those need a justification rather than just an impulse.
Katie Rose Pipkin: It is clear that you have a strong sense of design, and that your considerable skills in color, layout, and text work have paid off to your benefit here, in the form of a nice little book. I have very little to critique in that field, and think it is successful as an aesthetic object. That being said, I’m not so impressed by the content- the rehashing of 70’s playboy with phrases off twitter in service to a personal brand are off-putting to me at best. I think you’re good at advertising, but I’m not sure if I buy that what you’re selling is of value to me. I’m not telling you that you have to care – but if this were my personal brand, I’d think of ways that were a little bit more complicated to approach it.
Kyuha Shim: The typographic patterns are visually appealing and the french-fold binding works well. The typography, however, lacks relevance to Playboy images.
Golan Levin: It’s clear that you really enjoyed the process, and I’m glad you got a good taste of a powerful new tool. The book itself is repetitive, and seems conceptually arbitrary; the relationship between the pinup imagery and the typographic play is unclear to me, and seems like a private joke. I’d recommend a follow-up experiment where you just focused on content-driven computational manipulations of type, without the requirement to make a book.


arialy

arialy-book

arialy-book

Nick Montfort: It’s amusing to generate instructions, including recipes, and since a milkshake is a smooth combination of ingredients it may be particularly funny in this case. There is real academic work in computer-generated cookery and mixology, for instance the Pierre, Chef, Julia, the Mixilator, and a more recent UCSC mixology system. Also, see Harry Mathews’s Country Cooking for a good story told as a recipe. The concept is good here but the follow-though is not as good. The Markov-generated “Directions” are ungrammatical in a way that makes them less pleasant. Other techniques (grammars, combinations of appropriated texts, etc.) could have worked much better. The four ingredients are sampled independently, so it’s possible for two, or even all four of them, to be the same thing. Sampling should be done without replacement. Also, book design: No folios (page numbers), table of contents, running heads? Your first recipe is on the back (verso) of the title page? The whole framework did come together, despite these problems, and the idea is clever.
Katie Rose Pipkin: A cute idea that (to me) suffers from too much repetition with little reward for the reading. Recipes are a rich area for generative work (they follow a 1-2-3 kind of forumula that is easy to reproduce by automation), but the work then becomes: how to keep them from feeling formulaic? Given time constraints, I can see why you landed where you did. In truth, though, I think the failure here is in integration of the two sources; the hate-letters still read like hate letters and the milkshake recipes still read like recipes and the lines between sources are fairly clear. I would focus on how to generate systems that really speak to your goals- think about what you would do to make a hate-smoothie (do you add human hair? do you whisper condescendingly to the milk?) and revisit.
Kyuha Shim: I do not think this project makes sense.
Golan Levin: Fine start. I wish the recipes had more surprises; they’d be more enjoyable to read. How to achieve?


cambu

cambu-book

cambu-book

Nick Montfort: This is well-executed, with good design and production. It’s data-driven rather than algorithmically generated; okay. I just wonder in the end why your LA summer was interesting, to you or anyone else, and how the book focuses on its interesting aspects or brings new ones to light. I don’t mean this insultingly; we should always wonder what’s interesting about the subject matter of our work. And, it’s okay to do a project not knowing exactly why the subject is interesting … but one hopes to find out. Is your book mostly about you, LA, or Google? Could it be more about one of those in an interesting way? Would you need to make a different book for each reader to get the uncanny effect you mention? Could the process of creating the book, and generating the original data, be represented any better in the book itself?
Katie Rose Pipkin: Impressive both technically and conceptually, but falls flat in the actual final result- the emotional response you describe to seeing your deja-vu summer is unavailable to me, and with little other context the scattering of locations fails to build a story I care about. I would ask you to consider charting google maps paths that may be resonant for a public at large; for example, what if the locations were not /yours/ but a story we all know- MLK’s March on Washington, or the sites visited by the last retiring space shuttle, or any other story that has meaning to a culture, not just to you.
Kyuha Shim: It would be interesting to see multiple data sets, in addition to your location data, (e.g. weather, instagram photos) for remix.
Golan Levin: Attractive, and technically proficient, but dry. All the feels remain in your head; even, painfully so. If this object is unsuited to sharing with others, give yourself permission to say so.


catlu

Catlu – Book

catlu-book

Nick Montfort: Nice cultural engagement, and good to be expansive with ideas across cultures. I think the project invites some close reading and thinking about how myths are in some ways not compatible and in other ways can be combined. Why label it “whimsical” and use “mashup” on the title page? What if someone who was interested in world religion found it not so whimsical? If you want to combine two different source texts, consider techniques beyond the use of conditional probabilities on word pairs or triples (Markov chain generation). The method you are using now loses any connection between sentences, which is why pronouns appear (“she,” “they”) that have no referent. This is a good start and I encourage you to continue the project, understanding this text-generation technique and others in detail. Also, book design: No folios (page numbers), table of contents, running heads? Your first recipe is on the back (verso) of the title page?
Katie Rose Pipkin: As much as I’d like to like the result of this project, I just can’t get into it- the Markov chain results are too scattered for me to try to construct story, mythological or otherwise, and I can’t help but wonder if it is a responsible way to handle looking at similarities of cultural and historical text. Myths are weighty subjects, and I would be wary of treading into waters that might be called appropriative if shown at large. Perhaps it would be worth looking into some ways that classicists have already tried to analyze myth, for better or for worse- there are neat resources like https://sites.ualberta.ca/~urban/Projects/English/Motif_Index.htm that can become interesting data for generative projects. Using second-hand data has the benefit of a layer of objectivity or criticality; you can work with structures without becoming complicit. From a formal standpoint, I think you might benefit from imposing some structure on your markov chains- retaining a single subject could do wonders for legibility, for example.
Kyuha Shim: Your project inquiry is very interesting, but it is difficult to see how the generated stories retain the cultural and regional aspects. Perhaps you could include sentiment and text classification analysis of input and output texts.
Golan Levin: A very good start. Admittedly, the final result has that unmistakable “Markov chain” flavor. I consider it a success for a first experiment. Deserves to be read aloud.


claker

Claker- Book

source

Nick Montfort: This is a fine project, and seems to offer some insight about language, personality, and experience, even to those who do not know you. The cover image is suitable and pleasing. I particularly like that these emails were deleted as the project was done, so that now, only Google has them. This is a personal-data project with a bit of generation, although I would note that the use of JSON and p5.js seems a bit elaborate. I did an approximation of this (per line rather than per sentence) in a few seconds to my email with $ cd mail $ grep “sorry” sent-mail*.
Katie Rose Pipkin: A beautiful book, really. You’re successfully using personal material in a way that incriminates all of us- after all, who among us does not have an outbox full of apologies, both deserved and otherwise. I also think your solution to preserving the anonymity of your correspondences is clever, and leaves the reader with enough context to situate each sorry. My only real side note: I found the physical book lacking (unstapled 8.5×11 paper, folded) and hope you will pursue another printing in the future- I think it desrves it.
Kyuha Shim: Missing/incomplete at time of review.
Golan Levin: Brutal. Your design iterations really helped; the final result shows evident care. I’m still waiting to see the professionally printed version.


darca

Darca-Book

darca-book

Nick Montfort: The GitHub link does not work; I can’t see the code or read a PDF. There’s some evidence a bound book was created. Ask to have this grade raised if the code and PDF were handed in.
Katie Rose Pipkin: A simple, clean idea, perfect for the scope of a small project. That said, I can’t say that I was particularly enthralled by the content of this book- it isn’t one I’d sit down and read, however evocative the wording of each color. Perhaps it is an idea better suited to a shorter format, more digestible as individual moments than the weight of a single volume. Still, you did your best to make a book of it regardless, and I felt you deserved a little extra credit for the beautiful binding. Its clear you sunk a lot of time and thought into it.
Kyuha Shim: Missing/incomplete at time of review.
Golan Levin: Apart from the fact that the book was late, there are some urgent problems of execution here, chiefly related to your documentation. (1) You were asked to upload a PDF of your book, or a link to a PDF of your book. Without the PDF, it is impossible to read and evaluate your book: the tiny photographs from your bookbinding process are not sufficient to read and evaluate your book. Simple things like “uploading a PDF” are easy to do, and make a big difference in demonstrating that you are careful, respectful, and understanding of your reviewers’ needs. (2) Although you describe your inspirations, you did not describe how you generated the text of the book. What was the process? What sort of computational approaches did you use? (3) You were asked to provide your code. However, you did not embed your code. Furthermore, there is no code or repository at the Github link you provided in the blog post; it’s a dead link. (4) I’m writing this six days after providing you with the above feedback by email, and these issues have still not been addressed.


drewch

Drewch – Book

drewch-book

Nick Montfort: The technique that is sketched here has some potential and results in visually pleasing output. The PDF file is in bad shape (for instance, not rotated correctly) and in any case the book “must be printed and bound.”
Katie Rose Pipkin: The concept and content of this book is truly lovely, and I have little to say other than keep it up. Unfortunately, as the assignment was to make /and print/ a book, I feel that I’m really unable to give you the grade I would have liked. Even just printing on half-decent paper on any old printer and doing a stitch binding can make a beautiful object. I’d advise you in the future to not give in too quickly to the (admittably, very real) pressures of economy, and to think about the resources that are at hand.
Kyuha Shim: Needs more documentation. ‘Legible letters’ are not legible in your work right now. Might make more sense if viewers interact with this on screen (so they can zoom in). You can highlight important words through color with blend_mode rather than opacity.
Golan Levin: Exhausting to read, but I see what you’re getting at. I do wish you had found the energy to print it. What would you think about an approach like automating what Tom Phillips does in ‘A Humument’?


guodu

Guodu-Book

guodu-book

Nick Montfort: “Do people feel like I just did this by hand in InDesign, no scripting? or is it obviously programmed? or both?” I would say the former, which does not mean you failed to fulfill the assignment, but that the generative abilities of the computer were not used to accomplish something new. I like the ABC book concept, the appropriately colorful presentation, and the idea of bringing typography to very young readers. It could (and should) be worked on further. It seems a bit awkward that some letterforms, and important features of them at times, are hidden behind others. I am not sure that guessing which font is which is a good pedagogical strategy. But there are many pleasing things about this book.
Katie Rose Pipkin: First off; this is a pretty book. The obvious care in the printing and binding shows, and your eye for layouts (generative or otherwise) is clear. I like the conceit of Baby’s First Font Book, and it does have the aesthetic of a primer. My tastes skew more towards the conceptual (‘but what does it mean?’ etc), but this is a nice project regardless. I think your self evaluations of where to go next are accuate, and I could see more font specification and color options helping. Keep it up!
Kyuha Shim: The legibility could be improved. If you color-code each typeface, viewers may be able to intuitively figure out which one is which.
Golan Levin: If you had solved the problem of selecting fonts whose names began with the letter shown on each page, I’d give this otherwise excellent book a much higher grade.


hizlik

hizlik-book

hizlik-book

Nick Montfort: This is a good metaphor and a pleasing visualization. As a personal-data project this one stands out by not insisting too much on private texts and language and presenting images that are pleasing and that provoke the imagination, even if their basis is not understood. It’s possible that the watercolor effect is corny, but it does balance the rectilinear buildings above.
Katie Rose Pipkin: My first thought flipping through this book in the studio was that it was beautiful, but I sure wished it was based on data of some kind. Later, reading your project page, I found that it /was/ and I felt both a bit stupid for missing the markers of numbers of messages/attachments/etc, as well as disapointed I’d been allowed to miss them. I find I often err on the side of underexplanation in my own work, hoping my objects will explain themselves for me. Often, what I need is just a paragraph of preface- something I hope you’ll consider including in subsequent printings. Otherwise, I’m happy for you. It is a sweet and kind project that draws metaphors between growing care with another person and the construction of landscape- I think the images are effective and are succesful in surviving in a digital space, despite their more-physical, watercolor origins. Congrats!
Kyuha Shim: The construction of cityscapes with text is interesting. Would love to see in-depth descriptions of how you set the rules for details (e.g. width of images, complexity of building or distance between buildings). Also, you can consider color as a parameter that varies upon emotion.
Golan Levin: The more I learn about it, and the more I think about it, the more I admire this project. Great process, great motivation. I wish it were somehow possible to both maintain and reveal the mystery of the book’s process.


jaqaur

Jaqaur – Book

jaquar-book

Nick Montfort: Seems like a good, ambitious project. Although my levels of time and talent will not allow me to try out the songs, I am amused at the lyrics. Whether or not the musical results are of the highest quality, trying to generate lyrical music and encountering what’s involved is great for this sort of book project. The book could use a title page.
Katie Rose Pipkin: I’m impressed. This is a serious undertaking in a small amount of time, and I think you’ve done a great job with material that is under-used in general. Congrats. For the future- I would love to see someone trying to play or sing one of these. Have you tried? To me, some of the most interesting spaces in bot and generative work is when they swing back around to interface with humans again.
Kyuha Shim: Great work and process! Would be great if you invite participants to play and sing your songs.
Golan Levin: Excellent work: a high-quality and self-evidently passionate investigation. Deserves to be performed.


kadoin

kadoin-Book

cover-1

Nick Montfort: I am glad there was some engagement with text as well as image. However neither text nor image accomplish much. The text is not composed well, even if the goal is to present amusing nonsense. Try a different generative or combining technique or try different source texts. If the goal is humor, why is it funny for robots to fall in love or write to each other? The book could use a title page. No code or documentation of finished book provided.
Katie Rose Pipkin: User manuals and Shakespeare could get old fast, but I think your saving grace here is the rhyming couplet; they are short and sing-song enough that they retain a certain charm. Congrats on restraining your text away from becoming noise, and the illustrations are cute. That being said, I’d ask you to reach a little further than robots and Shakespeare- there is a massive weight of human history to pull data from, and it needn’t be the most obvious sources. Widen your gaze and I think your work could trascend ‘cute’.
Kyuha Shim: Confusing. ‘More documentation’ has not arrived. (November 2)
Golan Levin: This is very strong, comprehensive (i.e. all-around) technical work: the combination of flipbook with generated poetry; the use of both Markov chains and rhyming. Congratulations. That said, while the book is well-conceived, it unfortunately become somewhat monotonous: the animation follows predictable pattern; the poems seem so tightly patterned as well, and lack sparkle. Perhaps the required length of a flipbook is too much for the limits of the expressive range of your text algorithm? Or perhaps the text should be even more dull. Apart from this, your online documentation is missing important requirements.


kander

kander – book

kander-book

Nick Montfort: Definitely the best book of computer-generated dick pics that I have seen. It is amusing to computer-generate something so crass but also bodily (as opposed to platonic solids and other ideal shapes). The uniformity of the illustration, with differences in attitude and size, hightens the humor of the tweets. A good example of how a computer-generated element is added to amplify existing humor in quoted/appropriated text.
Katie Rose Pipkin: I’m impressed with the dick generator; truly. I hope you continue to work on it and add all of the natural variation that this kind earth has granted humanity. That said, I’d expected to get a good laugh or two out of your book; the title was especially promising. Instead, I ended up feeling kind of sad. Obviously, there is no accounting for taste, and what you find funny is unlikely to be the same as me, but the lack felt noticable, or perhaps intentional. I see little wrong with laughing at the words of (as you called it) douchebaggery, but I suppose I found myself biting my lip at the prospect at laughing at sad, lonely people who had little intent to have their words recycled as a comedic art project. Given that you’ve given yourself reign to pick the posts, I might ask that if you continue to work with found text you consider the morals of public space, and what is free to use, when.
Kyuha Shim: Interesting concept and visualization. Whether the italic script font works well with your illustrations is questionable.
Golan Levin: Good work, with a pleasing premise to the investigation, and: congrats on finishing first — though my question to you would be: what would be necessary to make this into really excellent work? The penises, while initially charming, quickly prove lacking in variety. The graphic design feels weak, and needs a tuneup.


keali

Keali-Book

keali-book

Nick Montfort: When you see a humorous juxtaposition like this, you have to wonder who is being made fun of. The Japanese anime studio? The people (fans?) who subtitled the shows? Japanese culture? Our memories of childhood? Or if there’s no making-fun, why is this funny? It does seem like this is making something incongruous more inconguous. Is it better to Markov-generate titles, to just pick some other subtitle (the examples you show don’t include many that are obviously new texts), or just write your own like in Woody Allen’s What’s Up Tiger Lily? Well, I’m left with lot of questions about the project, some of which at least should have been better addressed. I don’t object to cross-cultural work, appropriation, and so on, but as it stands the project leaves me wondering. As for the book design, you made a book where the cover looks the same as any of the inside pages, and it has no title page or other apparatus. Nothing in the PDF even indicates who it is by, and in this condition it risks looks like a printing/binding mistake rather than a book.
Katie Rose Pipkin: At my first read, I had a hard time drawing any context between subtitle and screenshot- they seemed randomly placed. Reading your documention, I found they were indeed not expressly related, and that you’d had to generate each side (image and text) separately. I think this shows, and is a weakness here; perhaps consider tools like image recognition to help tie your words and images together. Still- for all the convolution of process (and lack of integration between actual screenshot and subtitle), you have made something that has some genuine humor in it, and I think is an entertaining flip through that retains the qualities of bad subtitles at large. Have you seen David Lubblin’s TV Comment Bot? (https://twitter.com/TVCommentBot) It might pose some inspiration on ways to go about a project like this with a bit more automation.
Kyuha Shim: Making random pairups to achieve ‘light and comical’ almost seems too easy! It would be more thoughtful if there were conditionals to check the relation between imagery and captions. Perhaps having black background instead of white would make your spreads more legible.
Golan Levin: Satisfactorily executed and certainly well-documented, but had there been time, it would have been nice to investigate processes that could have generated captions that were more closely tied to the image content. Randomness seems like the easiest way out; there are good solutions that wouldn’t require strong AI.


kelc

Kelc-Book

kelc-book

Nick Montfort: This is aleady a classic use of text generation, as in the bot @WhitmanFML (Walt “FML” Whitman). There are some good results in this case, although perhaps the racial politics of a poet “vs.” rapper mashup should be dealt with in the project. The book design seems lacking. No title page? Why is it best to have only one text per page, given that you can generate very large numbers? Why this number of texts? (You cover says “90” but your final book block has 70.)
Katie Rose Pipkin: I think this book suffers from a common problem in generative processes, which is that in this breed of conflation (erowid and Nietzche, Marx and Taylor Swift, two headlines), the ones that work brilliantly are not always the ones that sound like they will be good. Unfortunately, I think this one does not work as well as one might hope- there is little for me to gain between jumping from ‘music the food of love’ to contemporary rap, perhaps because they remain so obviously seperated by their time and contexts that there is no moment of blurring- I see the cut between sources clearly. I would suggest using this structure you’ve built with other media, maybe experimenting until you find one that allows the content to make something new.
Kyuha Shim: Your explanation about how you merged different text sources from rappers and poets could be more thorough. When breaking lines, you can use ‘balanced ragged lines’.
Golan Levin: Good start: many amusing moments.


krawleb

Krawleb-Book

baon3-1024x576

Nick Montfort: Cover is well-done, pages look all right but no folios (page numbers)? No title page? I appreciate your trying out this synonym-related technique, but the results are not very compelling and I’m not sure why these poems were presented. Why are they shown “intact” first when some (particularly very famous ones) would be known to readers? Why italicize the changes rather than let people wonder?
Katie Rose Pipkin: It is nice to see someone approaching text generation and not going straight for Markov chains- I personally think processes like synonym swapping can be a lot more interesting, and certainly get less play. I think your project had a lot more capacity than what it ended up producing, however- the slowly mutating individual words didn’t really effect how I read the poems as structural whole, and I lost some interest after I realized what was happening. In truth, I simply don’t want to read the same poem 5 times, even if a few pieces have changed- perhaps increasing the number of words shifting over time could remedy this apathy. Still, good choice on process, and the moments where it shines are charming.
Kyuha Shim: Elegant approach. It would be interesting to see the progression of swapping synonyms shown on a single page. An increase in leading (line-height) would make those italicised words more eye-catching.
Golan Levin: I respect your investigation. There are many well-considered aspects to your process, with results that vary between fascinating, boring, and machinically error-ful. I wonder, however, about your choice to pursue progressive corruption versus an approach that would have showed numerous alternative versions (thesaurus-izing); it’s unfortunately dull to read the same thing so many times, with such minimal changes.


lumar

Lumar-BookOfKindness

lumar-book

Nick Montfort: This project clearly involved a lot of effort and learning, although the texts that resulted are not extraordinary and the automatically scattered verbs impede, rather than enhancing, reading. It’s okay to have discovered that through experiment; it would be great to find a way to “illustrate” a text automatically like that, one that is really effective. The IDs to which each quote is attributed seem bizarre, maybe in a good way. See Nick Thurston’s Of the Subcontract, which is more pointed in documenting the time spent and money earned for each poem. This project seemed to have a lot of little components that were fairly separate; see if you can figure out a better overarching idea and framework the next time you take on something like this.
Katie Rose Pipkin: A very cute book, both in concept and design. Mechanical turk is a nightmare of a place, and I think there is interesting territory in producing tasks that are humanizing. Your binding is well-done, and I appreciated the care to the outside edge of the book. In a sort of preverse way, I think a similar strategy could be used for material that was less uplifting- mechanical turk tasks that criticize the structure of the labor being done there, or what it is like to seek employment in that way. I’d consider ways your existing strategies could be applied to critical works with other things to say.
Kyuha Shim: How you consider a book as a 3D object is very good! But you could improve the typography. Especially large texts appearing on the right spreads need major fixing. Color of the texts can be dictated by something other than its page number.
Golan Levin: Very interesting concept, strongly conceived process, and truly poignant results. I’m unexpectedly disappointed by the graphic design; the right-hand pages are weak.


ngdon

Ngdon-Book

ngdon-book

Nick Montfort: Very nice and a good concept, since mythical beasts (and also actual biological ones) exhibit combinations of other creatures’ attributes. The bestiary is an established literary form. Good selection of a high-level topic and good work in the generation of fauna. Nice connection to the classic Chinese book, too. You could more richly model what causes creatures to take different shapes, of course, and could auto-generate verbal descriptions of them rather than just names.
Katie Rose Pipkin: Congratulations: I’m impressed. This is a pretty remarkable project to execute in a week. If you are interested, I hope you continue to work on this generator, adding variation over time as you have it. You mentioned in your project description that you are considering continuing this idea into geography and plants, which I think could be quite compelling. I think you should look at Emily Short’s procedural work, particularily this one- https://drive.google.com/file/d/0B97d5C256qbrOHFwSUhsZE4tU0k/view. I’d also suggest submitting this to procjam (http://procjam.com/) if you’d like a more public response. I think it deserves it!
Kyuha Shim: Impressive work! Perhaps viewers would appreciate, if you include a brief description regarding the relations between island, animals and their names before or after p.2.
Golan Levin: Impressive work. Would be an interesting challenge to generate descriptions (biological or mythological) about the creatures. Unfortunately, he creatures’ eyes look ‘dead’ to me, and would be worth revising. (Also — nice use of Mitchell’s algorithm.)


takos

takos-book

takos-book

Nick Montfort: There is a good bit of academic work in humor and joke generation, recently by Tony Veale for instance. This attempt in this project is not generally a bad idea as it guarantees a sort of cohesion but allows the topic to change, maybe amusingly. However it is not carried out very well in this book — how it works in bot format is another question. Your book repeats the same tweets time after time, which isn’t necessary or a good idea. There is way too much reminding people of the Twitter origin of the text; why are there (fake? changing?) names attributed to each text, for instance? The note at the beginning is unnecessary. You could mention the origin of the statements at the end if you think you need to. Also, there is no title page to your book. There is a rich and well-known tradition of joke books that you could refer to, perhaps making a colorful cover as if your book were for kids, but your book just has a title in black on a white cover. If you want to take advantage of the book format, you could put the question on the right (recto) and have the reader find out the answer when turning the page.
Katie Rose Pipkin: Cute idea that fails to really work for me. I think you could use some simple rules to avoid repeition of the same tweet content, as well as recognizing when your ‘because’ is not followed by enough other text to form an answer. I know the book is called ‘attempts at jokes’ and not ‘jokes’, but to me it is failing structurally rather than because of the content. I’d revisit the rules by which you are gathering tweets, see where things fall down, and write some cases to circumnavigate them.
Kyuha Shim: Clear arrangements of all information. I can understand your reason of taking tweets upon ‘why did’ and ‘before’, but would appreciate it more if you incorporate a few other structures for jokes.
Golan Levin: Good job, though gem juxtapositions are rare. More work is needed to improve the ‘quality’ of the non-jokes, while I certainly appreciate that it’s difficult to do so. The layout leaves something to be desired.


tigop

tigop-Book

Nick Montfort: A simple example of grep poetry/Twitter search but presented visually very well in terms of the page layout, and with a nice binding, too. This term matches an interesting collection of texts, too, and the result is interesting to read. I’m not sure it manages “to unveil what the Twitter population’s thoughts on gaslighting were” because these tweets are taken out of context — they might be replies, captions to photos, quoted retweets, and so on. But without being documentary they are still worthwhile.
Katie Rose Pipkin: First off- your binding is beautiful, and the physical object deserves some real recognition. I wish I felt the content was as solid as its cover. I’m glad you pursued something that could be considered political, but I feel it gains little from being pulled off twitter- the same tweets and material are available to me in the search bar, and you’ve offered no particular stance or additional weight for your part. Perhaps even doing some simple scrubbing of text- removing links we can’t follow and handles we can’t click- would help recontextualize it in book format, and let it be about the word and concept and not just twitter.
Kyuha Shim: It’s not clear what was the intention of taking ‘gaslighting’ as a keyword. Typography could be improved.
Golan Levin: Acceptable work, and nicely bound, though a late submission. Strikes me as lacking a strong enough point of view, and needs editing, or more ideas than a mere search for a term.


xastol

Xastol – Book

xastol-book

Nick Montfort: The layout here is generated, so I suppose this small book is applicable, but it’s almost as if these were 24 random tweets. I learn almost nothing about how people use the #RIP hashtag that I hadn’t guessed. Nothing about people in general, language, or Twitter. Why October 21, 2016, other than an impending due date? I appreciate the connection between the pages images and the concept, but much could have been done to make the book readable, interesting, and provocative.
Katie Rose Pipkin: I’m afraid I liked the concept more than the actual result; this is a sad but common side-effect with generative projects, where sometimes the one-liner description is more compelling than the material that comes of it. In the book’s best moments, there was humor; in its worst (which was for me, the cut-off tweets with links not much else) I felt like I was looking at noise. Still, congrats on getting it printed, with cute procedural graphics no less- my advice for the future is to perhaps keep thinking of rules that help you to cut out content that is not interesting to compelling to your idea, and to grab that which is.
Kyuha Shim: Nice choice for the typeface! It could be more interesting if there were variations of tombstones.
Golan Levin: A bit facile, and not as memorable as it could be. There’s lots of ‘junk’ in there; I feel like the challenge in a project like this is to create a computational “editor/curator” with a strong voice, and it’s not there. As I discussed with you previously, I feel like you missed an opportunity to use the assignment to address your core interests, e.g. film — why not generate a screenplay, etc? This project is competently done, but also feels like you’re not as invested in it as you could be.


zarard

Zarard-Book

screen-shot-2016-11-14-at-1-30-19-pm

Nick Montfort: This is a nice use of computation to engage with a photographer’s work and give a different sort of perspective on it. As noted, the information at hand is about the photos rather than the people or how they looked to anyone other than this camera. I think sorting the face details by brightness may not have been the best approach, because this method itself suggests that “color” (brightness/darkness) is the primary axis for organizing people. It would be interesting (if much harder) to organize the faces based on where they were looking, for instance. Still, lots to think about and look for in this project.
Katie Rose Pipkin: I commend you for working with an interesting dataset, one that has its own inherent strengths. Many others seemed to be fighting to make twitter or flickr feel personal, or meaningful. It is wise to use source material that has its own unique voice already, and congratulations for seeing that. The work you’ve done on top of the quality of material you’ve brought to the table seems fairly minimal, but it has a strong argument to it. Keep it up!
Kyuha Shim: The description includes only self-evaluation and execution methods. What is the project objective and concept? What is demonstrated, apart from sorting portraits?
Golan Levin: An extremely interesting investigation — though, impaired by a somewhat slack execution. For example, I found it necessary to add (to your blog post) information about who Teenie Harris was, so that a typical reader (and potentially — the other reviewers) would be able to understand what they were looking at. Also: The digital and printed objects suffer from low resolution and very poor print quality — e.g. I happen to know that you could have extracted the faces from higher-resolution versions of the images, and the Espresso book machine does not reproduce the images well. I also really don’t understand your peculiar and almost disrespectful choice of font (Cooper? — Really? The Diff’rent Strokes sitcom font? The Tootsie roll font?). Also, your documentation does not contain your code, as requested in the assignment statement. In sum, this is a fine project, but I wish there were some more ‘care’ taken to make a high-quality presentation.

Comments Off on Feedback on Books Posted in

Darca-Book

My book is called Stripes. It contains a stripe of colors on each page.

dd

I was fascinated by this perception study: Colour is in the eye of the beholder. I realize that colors are not only just shades or hues or RGB values; they have very strong relationships to feeling, smell, taste and memory. So I wanted to represent the concept of color by evoking them with words, so that the reader would “see” the colors by imagining them. They can be completely personal and generic, different from person to person, depending on their experience or understanding of the descriptions. Just imagine seeing the stripe that contains the colors of “cornflour sparkle“, “spider web weave“, “old Oriental rug burn“, “glisten shimmer pretzel“, “glow field of clover” and “iceberg peanut butter Arctic landscape“. You see what I mean.

A closer look at my bookbinding process:

Print and cut with an edge cutter:

img_4528   img_4529

Organize and press between two pieces of wood blocks:

img_4530   img_4531

Brush PVA glue into the spine of the book. Shorten the drying time by blowing hot air on the surface until the glue is dry. Repeat three times:

img_4536   img_4541

That’s it!

img_4543   img_4544

img_4545   img_4548

Github: https://github.com/DarcyCao/60-212/tree/master/book/

Written by Comments Off on Darca-Book Posted in Book

Claker- Book

The book ‘so sorry’ compiles all of the emails I sent from 2011-2016 in which the word ‘sorry’ appears at least once, in reverse chronological order. There were 214 emails that met this criterion, from two different Gmail accounts of mine. Each page of this book contains a redacted version of one of these emails, in which only the sentence that contains the apology is left visible, without its context. Each page also includes the time and date each email was sent. After I included an email in the book, I permanently deleted it from my email account.

img_2277-mov

sosorry_spreads

The emails were retrieved ‘by hand’ in order to ensure that no sensitive information was copied over. The date, time and full content of the emails were stored in a JSON file. Using p5.js I redacted all sentences that did not include the word “sorry” in them. The book was then laid out computationally in Adobe InDesign using Basil.js.

PDF of the book: So Sorry (PDF)


Code for Adobe InDesign layout with Basil.js



Redaction code in p5.js


Written by Comments Off on Claker- Book Posted in Book

tigop-Book

PDF: gaslightingbook.pdf

(pdf of book above)

Using the Temboo API service, I decided to retrieve tweets which use the words gaslight, gaslights, gaslighting, gaslighter, gaslighted, etc. I wanted this to be a tool to unveil what the Twitter population’s thoughts on gaslighting were, and as an outcome to this experiment, I received both political commentary as well as personal history of those who claim that they have been gaslighted. I was expecting to see results relating to abusive or toxic relationships and psychological torment, but I was surprised to see tweets on Hillary Clinton and Donald Trump show up. These results made the content of the book extremely reflective of the time we are currently living in, and I thought that was something which made this collection of tweets special. Also, I was unaware of the fact that trans and queer communities might show opposition or hostility towards one another — something that I had learned after reading the retrieved tweets.

I wish I had played around more with imagery, but I was able to play around with the formatting of the font/color/size of the words (which was a bit frustrating — I first tried using Basil.js and InDesign, but because the tweets had many unusual characters, Basil.js freaked out. Due to time constraints, I simply used Processing to generate a PDF of my book.

 



// Set up text strings
String searchText, tweetText;

// Create a JSON object to store the search results
JSONObject searchResults;

void setup() {
output = createWriter("gaslightTweets56.txt");
// Set a search term and instructions
searchText = "https";

// Display initial tweet
runTweetsChoreo(); // Run the Tweets Choreo function
getTweetFromJSON(); // Parse the JSON response
timeKeeper();

}

void runTweetsChoreo() {
// Create the Choreo object using your Temboo session
Tweets tweetsChoreo = new Tweets(session);

// Set Profile
//tweetsChoreo.setCredential(Gaslighting Trackr);

// Set inputs
tweetsChoreo.setQuery("gaslighted");
tweetsChoreo.setConsumerKey("XXXXXXXXXXX");
tweetsChoreo.setAccessToken("XXXXXXXXXXX");
tweetsChoreo.setConsumerSecret("XXXXXXXXXXX");
tweetsChoreo.setAccessTokenSecret("XXXXXXXXXXX");

// Run the Choreo and store the results
TweetsResultSet tweetsResults = tweetsChoreo.run();

// Store results in a JSON object
searchResults = parseJSONObject(tweetsResults.getResponse());
}

void getTweetFromJSON() {
JSONArray statuses = searchResults.getJSONArray("statuses"); // Create a JSON array of the Twitter statuses in the object
//JSONObject tweet = statuses.getJSONObject(0); // Grab the first tweet and put it in a JSON object
for(int i=0; i

Layout code (Processing):


import processing.pdf.*;

void setup() {
int time= millis();
background(255);
size(612,792, PDF, "gaslightingBook.pdf" );
//noLoop();
//beginRecord(PDF, "gaslighting1.pdf");

}
//void draw(){
// The font "andalemo.ttf" must be located in the
// current sketch's "data" directory to load successfully

void draw(){
//size(612,792);
PGraphicsPDF pdf = (PGraphicsPDF) g;
PFont tweetFont = createFont("ChaparralPro-Bold",12);
PFont cuckFont = createFont("AR DARLING",23);
PFont timeFont = createFont("Castellar",23);
PFont subheadingFont = createFont("Bell MT Italic",12);

textFont(cuckFont);fill(0);textAlign(CENTER,CENTER);textSize(32);text("Gaslighting Trackr",width/2,height/2-80);
textFont(subheadingFont);fill(0);textAlign(CENTER,CENTER);textSize(28);text("\nand the American Psyche",width/2,height/2-70);
pdf.nextPage();//p1

pdf.nextPage();//p2
textFont(subheadingFont);fill(0);textAlign(CENTER,CENTER);textSize(12);text("\n\n\nCompiled by Teenygoop \n2016",width/2,height/2-70);
pdf.nextPage();//p3

pdf.nextPage();//p4
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\n RT @TicketmasterUK: .@springsteen tells @BBCRadio2 if he needed a stand-in \nhe'd pick @thebrianfallon.\nWe second that! >> https://t.co/InmAU…"+
"RT @TicketmasterUK: ICYMI: \n@thebrianfallon chats to us about his record and he \nupcoming UK tour >>\nhttps://t.co/AHVDCu3wKb" +
"\nRT @dhabitahzainal: @farahNAKATA hello! You can get it at Gaslight Cafe, \nBukit Damansara or anywhere that I'll be performing next 😀 "+
"\nwhere…\nShoutout to all the men in my life that have ever tried to gaslight me. \n@farahNAKATA hello! You can get it at Gaslight Cafe, Bukit Damansara or anywhere that \nI'll be performing next 😀 where are you staying?"
+"\nRT @TicketmasterUK: ICYMI: @thebrianfallon chats to us about his record and he \nupcoming UK tour >> https://t.co/AHVDCu3wKb"+
"\nCuándo alguien le explica a uno que es el Gaslight y uno se siente identificado...",30,30);

textFont(cuckFont);
fill(0);textAlign(CENTER,CENTER);text("\nOnly a cuck wouldn’t know that the original film \nversion of Gaslight was directed by Thorold Dickinson",width/2,height/2-100);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nFind Gaslight of St.James @gaslightstjames on https://t.co/SaCcOcn59y"+
"\nRT @TicketmasterUK: .@springsteen tells @BBCRadio2 if \nhe needed a stand-in he'd pick @thebrianfallon. \nWe second that! >> https://t.co/InmAU…"+
"\nRT @TicketmasterUK: .@springsteen tells @BBCRadio2 if \nhe needed a stand-in he'd pick @thebrianfallon. \nWe second that! >> https://t.co/InmAU…"+
"\nRT @tuckersworld09: @JonRiley7 @LosinDonald \nI don't think I've seen anyone gaslight as patently and frequently as trump."+
"\nRT @TicketmasterUK: .@springsteen tells @BBCRadio2 if \nhe needed a stand-in he'd pick @thebrianfallon.\nWe second that! >> https://t.co/InmAU…"+
"\n#NowPlaying  Handwritten by  The Gaslight Anthem.  \nListen https://t.co/3fPcyvproZ  Stream https://t.co/d1gncMyqHI \n#ClassicRock #Rock #radio"+
"\nRT @TicketmasterUK: .@springsteen tells @BBCRadio2 if \nhe needed a stand-in he'd pick @thebrianfallon. \nWe second that! >> https://t.co/InmAU…",
width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n \n STOLEN:10/27/2016 01:46:23",width/2+270,height/2+300);
pdf.nextPage();//p5

textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nIm not gonna post personal shit on here but I just think everyone should read this and be aware."+
"\n https://t.co/aviRqlsTHp"+
"\nRT @goldengateblond: There is literally no other way to interpret it, you gaslighting freak. \nhttps://t.co/hnKL80baaj"+
"\nRT @Piquerish: @nia4_trump @jamesbott007 Do NOT believe the LYING, GASLIGHTING, \nmanipulating PRESS! EVER! \nGO AND VOTE!!! https://t.co/tpn9n…"+
"\nRT @shannonrwatts: I'll publicly announce who I'm voting for and denounce his opposition, \nbut under no circumstances will I endorse him! #g…"+
"\nRT @goldengateblond: There is literally no other way to interpret it, you gaslighting freak. \nhttps://t.co/hnKL80baaj"+
"\nRT @Elixicana: Y'all seeing that pattern here? Writing people off as sensitive as a way to \ntry to make them seem weak, pretty much gaslight…"+
"\nRT @shannonrwatts: I'll publicly announce who I'm voting for and denounce his opposition, \nbut under no circumstances will I endorse him! #g…"+
"@NationalismRise that woman is groace. \nIt has to be a massive gaslighting operation on white males.\n@FrankConniff @jasoninthehouse @realDonaldTrump ",30,30);

textFont(cuckFont);
fill(0);textAlign(CENTER,CENTER);text("\nHow in the actual fuck is voting for someone \nNOT an endorsement?? GOP Gaslighting 101",width/2,height/2-80);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nRT @shannonrwatts: I'll publicly announce who I'm voting for and denounce his opposition, \nbut under no circumstances will I endorse him! #g…"+
"\nRT @goldengateblond: There is literally no other way to interpret it, \nyou gaslighting freak. https://t.co/hnKL80baaj"+
"\nRT @shannonrwatts: I'll publicly announce who I'm voting for and denounce his opposition, \nbut under no circumstances will I endorse him! #g…"+
"\n@karaschlegl I mean, he was good looking and all but \nhe's probably been gaslighting you the whole time?"+
"\nRT @shannonrwatts: I'll publicly announce who I'm voting for and denounce his opposition, \nbut under no circumstances will I endorse him! #g…"+
"\nRT @shannonrwatts: I'll publicly announce who I'm voting for and denounce his opposition, \nbut under no circumstances will I endorse him! #g…"
,width/2+270,height/2);

textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n \n STOLEN:10/27/2016 01:53:52",width/2+270,height/2+300);

pdf.nextPage();//p3

textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nRT @goldengateblond: There is literally no other way to interpret it, \nyou gaslighting freak. https://t.co/hnKL80baaj"+
"\nRT @goldengateblond: There is literally no other way to interpret it, \nyou gaslighting freak. https://t.co/hnKL80baaj"+
"\nRT @goldengateblond: There is literally no other way to interpret it, \nyou gaslighting freak. https://t.co/hnKL80baaj"+
"\nGaslighting too fucking overpowered."+
"\nIm not gonna post personal shit on here but I just think everyone should read this and be aware."+
"\nhttps://t.co/aviRqlsTHp"+
"\nRT @goldengateblond: There is literally no other way to interpret it,"+" \nyou gaslighting freak. https://t.co/hnKL80baaj"+
"\nRT @Piquerish: @nia4_trump @jamesbott007 Do NOT believe the LYING," +"\nGASLIGHTING, manipulating PRESS! EVER! GO AND VOTE!!! https://t.co/tpn9n…"+
"\nRT @shannonrwatts: I'll publicly announce who I'm voting for"+ "\nand denounce his opposition, but \nunder no circumstances will I endorse him! #g…"+
"\nRT @goldengateblond: There is literally no other way to interpret it,"+ "\nyou gaslighting freak. https://t.co/hnKL80baaj"+
"\nRT @Elixicana: Y'all seeing that pattern here?",20,20);

textFont(cuckFont);
fill(0);textAlign(CENTER,CENTER);text("\nWriting people off as sensitive as a way to try to \nmake them seem weak, pretty much gaslight…",width/2,height/2-60);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nRT @shannonrwatts: I'll publicly announce who I'm voting for and denounce his opposition,"+ "\nbut under no circumstances will I endorse him! #g…"+
"\n@NationalismRise that woman is groace. It has to be"+ "\na massive gaslighting operation on white males."+
"\n@FrankConniff @jasoninthehouse @realDonaldTrump How in the actual fuck"+ "\nis voting for someone NOT an endorsement??  GOP Gaslighting 101"+
"\nRT @shannonrwatts: I'll publicly announce who I'm voting for and"+ "\ndenounce his opposition, but under no circumstances will I endorse him! #g…"+
"\nRT @goldengateblond: There is literally no other way to interpret it, \nyou gaslighting freak. https://t.co/hnKL80baaj",width/2+270,height/2+20);

textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 01:56:48",width/2+270,height/2+300);

pdf.nextPage();//p5
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nRT @caitoz: Hillary trying to back claim Japan for America. \nLike a retrospective Columbus."+ "\nThis woman is a gaslighter extraordinaire. https…"+
"\nRT @LannisterLover: Wow. Never realized what a vicious, vindictice, manipulative,"+ "\ngaslighter @HeatherDubrow is until the Ireland vaca  #rho…"+
"\nRT @LannisterLover: Wow. Never realized what a vicious, vindictice, manipulative,\ngaslighter @HeatherDubrow is until the Ireland vaca  #rho…"+
"\nRT @LannisterLover: Wow. Never realized what a vicious, vindictice, manipulative, \ngaslighter @HeatherDubrow is until the Ireland vaca  #rho…"+
"\nWow. Never realized what a vicious, vindictice, manipulative, gaslighter"+ "\n@HeatherDubrow is until the Ireland vaca  #rhoc #rhoc11 #psycho"+
"\n@realtvwchr @Leosxpstl36 @HeatherDubrow she is a gaslighter shes always been a spiteful person \nbut no1 b4 Kelly wld go up against her"+
"@AllAboutTheTea_ #HeatherDubrow is a \ntypical #bully #gaslighter #itsabouttime some1 stood up 2 her \nI am with #KellyDodd on this 1 #alltheway",30,30);

textFont(cuckFont);
fill(0);textAlign(CENTER,CENTER);text("\n@JeraunHudson @TalibKweli \ninhumanity against man has gone on way longer. \nTry again gaslighter.",width/2,height/2-100);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("må være helt alene i din egen leilighet med abuseren din og"+ "han gaslighter deg sykt : ) artig"+
"\n@Oasis00000 #gaslighter"+
"\n@milesjreed"+
"\nIntroducing our new gaslighter in chief."+
"\nRT @DABridge22: Listening to Kellyanne Conway and seriously"+ "\nwondering what she is taking. #logicfail @cnn"+ "\nShe's a gaslighter extraordinaire…"+
"\nRT @DABridge22: Listening to Kellyanne Conway and seriously wondering what she is taking."+ "\n#logicfail @cnn She's a gaslighter extraordinaire…"+
"\nListening to Kellyanne Conway and seriously wondering what she is taking."+ "\n#logicfail @cnn She's a gaslighter extraordinaire, \nlike her boss."+
"\n@Ciocia oy gaslighter, you better ask yourself that question... \ncause the retardmeter is off the charts with you",width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 02:04:07",width/2+270,height/2+300);

pdf.nextPage();//p6
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nRT @333333333433333: trying to communicate openly & \nhonestly after years of being gaslit and \nmisunderstood by nearly every romantic prospec…"+
"\nRT @mtracey: The way the HRC campaign and its media allies have"+ "\"gaslit\"" +"\nthe public re: @wikileaks is despicable and will have long-lasting…"+
"\nive been gaslit and emotionally manipulated my whole life and"+ "\nseeing those things explicitly depicted but widely dismissed is terrifying lol"+
"\nRT @revasteenbergen: The early stages of being gaslit by a #narcissist"+ "\nhttps://t.co/x6H44Osm9M"+
"\nRT @s_green: I just googled"+"\"what percentage of people who think they \nare getting gaslit are actually getting gaslit?\""+
"\n1) You're not gaslit, you're gaslighted."+
"\n2) You're not gaslighted, you just met a pregnant pro-choicer, is all."+
"\nI just googled"+ "\n\"what percentage of people who think they are getting gaslit are actually getting gaslit?\"",30,30);

textFont(cuckFont);textSize(19);
fill(0);textAlign(CENTER,CENTER);text("\nRT @carolJhedges: '\'Enter the dangerous \ntwisting alleyways and gaslit \nthoroughfares of 1860s London\''"+
"\n❈MURDER & MAYHEM❈"+
"https://t.co/p14nZ8C…",width/2,height/2-90);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\n@sonnysgal4life I hope it's more Ashley/Sharon but w/ the whole"+ "\"I adopted lie\"" +"\n& not the whole"+ "\"gaslit to think I was pregnant\""+" 🙈😂"+
"\n@in_a_partydress @annasproul I had gaslit myself into thinking you had always been blonde!"+
"\nthis confirms the memories I thought were lies!"+
"\n@DocBrown_TV @CW_Arrow I'll spare him the 🔥, "+"\nbut he seems device-y. It's the EPs' 180 re: Olicity "+"\nthat has me feeling gaslighted (gaslit?)."+
"\n@vedia_lupae I've occasionally said"+ "\" I feel like I'm being gaslit\""+" \nwhen it was really just a disagreement I was panicking about."+
"\npersonal for a sec: I've been living in a mentally"+"\n/emotionally taxing and abusive AND gaslit situation"+" for over a year. I'm getting out but"+
"\n@TASK1ne He just gaslit a fellow creator at the"+ "\nHouse of Ideas who has been horrifically harassed."+ "\nHe has been trash since 2004."+
"\nRT @revasteenbergen: The early stages of being gaslit"+ "\nby a #narcissist https://t.co/x6H44Osm9M",width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 02:06:19",width/2+270,height/2+300);

pdf.nextPage();//p7
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nRT @stilllearning2b: 10 Reasons Being Gaslighted Is the Worst #narcissist https://t.co/Ki6ziUJgKF"+
"\nNoticing a trend, while canvassing out of state:"+
"\nWomen being gaslighted by husbands or boyfriends"+
"\nor bros, or fathers. #ItsAFromOfBulllying!"+
"\nOne thing I know for absolute certain:"+ "\nif you lie to spare my feelings, you have now hurt my feelings,"+ "\nlost my respect, AND gaslighted me."+
"\nRT @ApologyProject: Tracy was gaslighted"+ "\ninto believing she couldn't be a #dv victim"+ "due to her strength & \nmartial arts training https://t.…"+
"\n@amaditalks so you still were gaslighted by duolingo chat bot?"+
"\nRT @sunslammerdown: getaway makes no secret that he's abused,"+ "\ngaslighted, and manipulated tailgate; \ndenying it is literally just denying th…"+
"\nRT @sunslammerdown: getaway makes no secret that he's abused,"+ "\ngaslighted, and manipulated tailgate; \ndenying it is literally just denying th…",30,30);

textFont(cuckFont);textSize(19);
fill(0);textAlign(CENTER,CENTER);text("\n\ngetaway makes no secret that he's abused, gaslighted, \nand manipulated tailgate; \ndenying it is literally just denying the uncontested truth."
,width/2,height/2-90);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\n@netw3rk as someone who felt gaslighted by our use of \nrashad vaughn last season i really need this"+
"\nTime to be gaslighted by the \nDuolingo chatbot… #GirdYourLoins"+
"\nRT @jazzhandmcfeels: Shitlibs are so thoroughly gaslighted \nthey're now just inventing conspiracy theories. \nWe troll farm now! https://t.co/…"+
"\nRT @jazzhandmcfeels: Shitlibs are so thoroughly gaslighted \nthey're now just inventing conspiracy theories. \nWe troll farm now! https://t.co/…"+
"\n1) You're not gaslit, you're gaslighted."+
"\n2) You're not gaslighted, you just met a pregnant pro-choicer, is all."+
"\n@COM1CBOOK @Big5Army I think I'm \nbeing gaslighted."+
"RT @iCALLhelpline: Most people who are gaslighted \nusually aren't aware that this is happening to them. \nThe challenge is recognize it and st…"
,width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 02:16:54",width/2+270,height/2+300);

pdf.nextPage();//p8
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nGaslighters use emotional & \npsychological intimidation on us for selfish gain. \nHere's how to DEAL W/ THOSE BULLIES: https://t.co/gv11iUymcP"+
"\n@JRubinBlogger @KatiePavlich @henryolsenEPPC I can't wait till \nall you media gaslighters are no longer in media. \nYour Misandry is abhorrent."+
"\n@mitchellvii @DanaRuxi123 Good to hear a positive message \n- tired of hearing about the rigging, \nwonder if they're gaslighters!!!"+
"\n@GrundislavGames The gaslighters? \nThe Candle Wicks? The Torch Burners?"+
"\nRT @LC_Sun: When you've been raised by gaslighters it is almost impossible \nto deal effectively - that is, realistically - with gaslighters…"+
"\n...gaslighters... https://t.co/308XntAYff"+
"\nRT @High_n_Right: .@GOP blasts O-Care after premium hike ann.. https://t.co/78cnGeScMl  \nPro Gaslighters @NancyPelosi @BarackObama the typic…"+
"\n.@GOP blasts O-Care after premium hike ann.. https://t.co/78cnGeScMl  Pro Gaslighters \n@NancyPelosi @BarackObama the typical Lib incompetents",30,30);

textFont(cuckFont);textSize(17);
fill(0);textAlign(CENTER,CENTER);text("\n\n\n\n\nLGB people are master gaslighters of trans people. \nWe flight for and alongside them, they drop us, \nthen they ask why we never help them."
,width/2,height/2-100);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\n\n\nWhen you've been raised by gaslighters it is almost impossible to deal effectively \n- that is, realistically - with gaslighters you encounter"+
"\n@MsBlaireWhite @BeyondPhere Wait, gaslighters? Really? XD"+
"\n@oceandreams007 @PJLM @wikileaks @realDonaldTrump Sure."+ "\nNever engage beyond tagging and bagging. \nThey get paid per engagement. #Gaslighters"+
"\n@BwsCurtis @FloridaJeeper16 @THEJeepMafia \nMost are paid trolls with a script in front of them anyway. \nNot worth our time. Gaslighters."+
"\n@seanmdav MSNBC, CNN,ABC,CBS, NBC, NTY, WaPo, Wall St J, \nUSAToday, all are master gaslighters. \nBut they are pissing off Americans big time."+
"\nRT @nickbiscardi: MT @Amaris_Acosta: Rape culture not held up by rapists; \nheld up by"+ "\"devils advocates\""+", gaslighters,"+ "\"sincere questions\""+",…"
,width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 02:20:02",width/2+270,height/2+300);

pdf.nextPage();//p9
textFont(cuckFont);
fill(0);textAlign(CENTER,CENTER);text("\n@smolgerbil it's one of the \ngreatest gaslightings of all time"+
"\n\nSTOLEN:10/27/2016 02:24:48"
,width/2,height/2-100);

pdf.nextPage();//p10
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\n@CIBSEHeritage @CIBSE @SLL100 Still a few places \nleft on this walking tour https://t.co/ibPrPniHqS"+
"\n@KerrangRadio #NowPlaying #ListeningTo The Gaslight Anthem -"+ "\''45\""+
"\nGenuine question: can people gaslight you accidentally because \nneither of you understand who is the stupid one?"+
"\n@mushyyyy ugh worst. The headphone jack doesn't work on my \ndock so I just blasted everyone with gaslight anthem"+
"\nRT @TicketmasterUK: .@springsteen tells @BBCRadio2 \nif he needed a stand-in he'd pick @thebrianfallon. \nWe second that! >> https://t.co/InmAU…"+
"\nRT @TicketmasterUK: ICYMI: @thebrianfallon \nchats to us about his record and he upcoming UK tour \n>> https://t.co/AHVDCu3wKb"+
"\nRT @dhabitahzainal: @farahNAKATA hello! \nYou can get it at Gaslight Cafe, Bukit Damansara or anywhere that I'll be performing next 😀 where…",30,30);

textFont(cuckFont);
fill(0);textAlign(CENTER,CENTER);text("\nRT @MegoLand: Don't ever let someone gaslight you, \nno matter the degree of mental illness \nin which you do, or do not, suffer."
,width/2,height/2-90);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nShoutout to all the men in my life that have ever tried to gaslight me."+
"\n@farahNAKATA hello! You can get it at Gaslight Cafe, \nBukit Damansara or anywhere that \nI'll be performing next 😀 where are you staying?"+
"\nRT @TicketmasterUK: ICYMI: @thebrianfallon chats to us about his record \nand he upcoming UK tour >> https://t.co/AHVDCu3wKb"+
"\nCuándo alguien le explica a uno que es el Gaslight y uno se siente identificado..."+
"\nOnly a cuck wouldn’t know that the original film version \nof Gaslight was directed by Thorold Dickinson"+
"\nFind Gaslight of St.James @gaslightstjames on https://t.co/SaCcOcn59y"+
"\nRT @TicketmasterUK: .@springsteen tells @BBCRadio2 \nif he needed a stand-in he'd pick @thebrianfallon. \nWe second that! >> https://t.co/InmAU…"
,width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 02:26:09",width-50,height/2+300);

pdf.nextPage();//p10
textFont(cuckFont);
fill(0);textAlign(CENTER,CENTER);text("\n@darksuji With the implication that the new, \ninternalized narrative puts \nthe gaslightee under the power \nor influence of the gaslighter."+
"\n\nSTOLEN:10/27/2016 02:28:17"
,width/2,height/2-100);

pdf.nextPage();//p11
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("EVERYONE CALLING BLUE GREEN GASLIGHTED ME"+
"\nWas the entire #oregonstandoff jury gaslighted by A bundy's 3 days of testimony?"+
"\n@Lawrence right because @newtgingrich emotionally abused & \ngaslighted his wife into not caring. \nA REAL man wouldnt even suggest such a thing"+
"\nWhen a person who gaslighted you and spread malicious lies about you starts talking about peace online 👀"+
"\nhttps://t.co/oEJquGTvEO v"+
"\n@KeithOlbermann @GQMagazine Keith, one wonders about women voting for Trump."+ "\nAs in, are they being coerced somehow? Gaslighted? Conditioned?"+
"\nRT @sexy_orphan: I've been gaslighted my whole life"+
"\n@osutein I never talk about it because Apple has \ngaslighted me into thinking I am the screw up here"+
"\nThe @GOP gaslighted this country and its POTUS for 8 years."+
"\nNever, ever listen to these assholes ever again!!… https://t.co/1GIiGZaQ2o"+
"\nI've been gaslighted my whole life",30,30);

textFont(cuckFont);
fill(0);textAlign(CENTER,CENTER);text("\n\nRT @prosumer_report: Interesting piece: \nAre #millennials being gaslighted? \nhttps://t.co/vwAlGt06GW via \n@bornagainmin https://t.co/iTFOjUUugl"
,width/2,height/2-100);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\n\n\n@CAtoDC So where does agency/accountability start and stop \nwhen you've been gaslighted for 20 years?"+
"\nRT @jifoulger: @spoonseeker Are we gaslighted as a \ncommunity https://t.co/dSuVfzayHX"+
"\nInteresting piece: Are #millennials being gaslighted? \nhttps://t.co/vwAlGt06GW via @bornagainmin https://t.co/iTFOjUUugl"+
"\n@Girl1928 [Nods] Yep. I too have been gaslighted."+
"\nRT @dtissagirl: I'm pretty sure the \n@ArrowWriters are gaslighting Felicity. Also gaslighted: me;.",width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 20:26:44",width/2+270,height/2+300);

pdf.nextPage();//p12
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("RT @spinosauruskin: SINGLEHANDEDLY SOLVE EVERY ISSUE I CARE ABOUT \nBEFORE SOLVING THE ONES YOU CARE ABOUT"+
"\n-Every gaslighter on the internet…"+
"\nSINGLEHANDEDLY SOLVE EVERY ISSUE I CARE ABOUT \nBEFORE SOLVING THE ONES YOU CARE ABOUT"+
"\n-Every gaslighter on the internet  @Haram_misfit"+
"\nRT @lepas: @Blackmudpuppy lmfao. \nHe's a classic gaslighter. No wonder his wife left him."+
"\nWhy does no one tell Binky that JP is a gaslighter?"+
"\nRT @caitoz: Hillary trying to back claim Japan for America. \nLike a retrospective Columbus. \nThis woman is a gaslighter extraordinaire. https…"+
"\nRT @LannisterLover: Wow. Never realized what a vicious, vindictice, \nmanipulative, gaslighter @HeatherDubrow is until \nthe Ireland vaca  #rho…",30,30);

textFont(cuckFont);textSize(19);
fill(0);textAlign(CENTER,CENTER);text("Wow. Never realized what a vicious, vindictice, \nmanipulative, gaslighter \n@HeatherDubrow is until the \nIreland vaca  #rhoc #rhoc11 #psycho"
,width/2,height/2-80);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\n@realtvwchr @Leosxpstl36 @HeatherDubrow she is a gaslighter \nshes always been a spiteful person but \nno1 b4 Kelly wld go up against her"+
"\n@AllAboutTheTea_ #HeatherDubrow is a typical #bully #gaslighter \n#itsabouttime some1 stood up 2 her \nI am with #KellyDodd on this 1 #alltheway"+
"\n@JeraunHudson @TalibKweli inhumanity against \nman has gone on way longer. Try again gaslighter."+
"\nnår du må være helt alene i din egen leilighet \nmed abuseren din og han gaslighter deg sykt : ) artig"+
"\n@Oasis00000 #gaslighter"+
"\nRT @calypsoh13: @milesjreed"+
"\nIntroducing our new gaslighter in chief.",width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 20:32:36",width/2+270,height/2+300);

pdf.nextPage();//p13
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nRT @LadyPutz: Breaking: Oregon Jury Gaslights Entire Nation https://t.co/RwI1CtDxDY"+
"\nBreaking: Oregon Jury Gaslights Entire Nation https://t.co/RwI1CtDxDY"+
"\nGaslights & Stringlights https://t.co/Ne1ClpoHpI https://t.co/iUJWbooDDV"+
"\n@SamcFoster I really hate how in the end of the video she \ngaslights the dog 😒 NOT cool.... https://t.co/OliFRGCOTl"+
"\nRT @aliciafiasco_: It's such a damaging message to send \nthat affirms abusers and gaslights their victims further. \nIt has to stop. Abuse is…"+
"\nRT @aliciafiasco_: It's such a damaging message to send \nthat affirms abusers and gaslights their victims further. \nIt has to stop. Abuse is…"+
"\nIt's such a damaging message to send that affirms abusers and \ngaslights their victims further. It has to stop. \nAbuse is not tender/romantic.",30,30);

textFont(cuckFont);textSize(19);
fill(0);textAlign(CENTER,CENTER);text("\nAbuser Gaslights victims. In other news, \nAbuser tells everyone \nto concentrate on more important matters. \nhttps://t.co/SBlPkmaU8F"
,width/2,height/2-100);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nRT @fangirlsmash: Bendis has never been a true ally ever since he came to prominence. \nHe regularly gaslights and blocks anyone who decries…"+
"\nRT @gabriel_gmaraes: @jryangolden @Coreybez1 @NormOrnstein @jboxt1 \nHe gaslights press by accusing HRC of whatever \nhe just did. I was certai…"+
"\n@jryangolden @Coreybez1 @NormOrnstein @jboxt1 \nHe gaslights press by accusing HRC of whatever he just did. \nI was certain at Rep convention"+
"\nRT @fangirlsmash: Bendis has never been a true ally ever \nsince he came to prominence. He regularly gaslights \nand blocks anyone who decries…"+
"\nRT @altmodes: ppl already dismiss the fact that swerve gaslights, \nabuses, and/or enslaves red alert and ten, \nwe don't have to Hypotheticall…"+
"\nRT @altmodes: ppl already dismiss the fact that swerve gaslights, \nabuses, and/or enslaves red alert and ten, \nwe don't have to Hypotheticall…"+
"\nppl already dismiss the fact that swerve gaslights, \nabuses, and/or enslaves red alert and ten, \nwe don't have to Hypothetically Imagine this"
,width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 20:55:04",width/2+270,height/2+300);

pdf.nextPage();//p14
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nRT @FemalesLikeUs: Both #NarcissiticManipulation & \n#Transactivism use the abuse tactic of gaslighting \nto blame their emotional instability…"+
"\nRT @goldengateblond: There is literally no other way to interpret it, \nyou gaslighting freak. https://t.co/hnKL80baaj"+
"\nRT @d_seaman: Gaslighting, a"+ "\“functionally evil\""+ "candidate, and the fight for truth powered by \n@wikileaks — today’s video: https://t.co/Cpq…"+
"\nRT @d_seaman: Gaslighting, a"+ "\“functionally evil\"" +"candidate, \nand the fight for truth powered by \n@wikileaks — today’s video: https://t.co/Cpq…"+
"\nRT @d_seaman: Gaslighting, a"+ "\“functionally evil\"" +"candidate, and the fight for truth powered by \n@wikileaks — today’s video: https://t.co/Cpq…"+
"\nRT @goldengateblond: There is literally no other way to interpret it, you \ngaslighting freak. https://t.co/hnKL80baaj",30,30);

textFont(cuckFont);textSize(18);
fill(0);textAlign(CENTER,CENTER);text("\n\nRT @FemalesLikeUs: Suicide threats = Psychological Warfare \non an Individual Level."+
"\nTA's Suicide Gaslighting of Women = Psychological Warfar…"
,width/2,height/2-100);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nRT @goldengateblond: There is literally no other way to interpret it, \nyou gaslighting freak. https://t.co/hnKL80baaj"+
"\nRT @SSWomen_: Have you heard of gaslighting? #MaybeHeDoesntHitYou \nbut it's a red flag for a manipulative form of \nemotional abuse. https://t…"+
"\nGaslighting, a “functionally evil” candidate, and the fight for truth \npowered by @wikileaks — today’s video: https://t.co/Cpqu9RuARp"+
"\nRT @aroueno: Look at them gaslighting Karthik... \nI want to grab their throats. #theapprentice"+
"\nRT @cavaticat: @MadbanditRoy @kellyhoover27 \nDon't come into this thread with your weak-ass \n#notallmen gaslighting. We don't owe you a damn…"+
"\nRT @AlisonSpalding2: The DNC needs to be punished for being so ABUSIVE to the \nBernie & Trump people & for GASLIGHTING us."+
"\nShow them they d…"+
"\nJeebus. Gaslighting in court:"+
"\nUs: Federal facility."+
"\nJury: Who says?"+
"\nUs: Firearms."+
"\nJury: What firearms? https://t.co/UEPgRpBQOy",width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 21:08:21",width/2+270,height/2+300);

pdf.nextPage();//p15
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\n@RamonMurkader and then the left gaslit everyone into thinking the Contras were bad"+
"\nI've seen vines with millions of loops \nso putrid and devoid of humor that \nit made me feel like I was being gaslit."+
"\n@JuddLegum we'd get gaslit & \ntold they deserved it, ppl would wear t-shirts \n& make effigies. It would be...horrible."+
"\nRT @CodeWitchJen: I mean as kids we were \nconstantly gaslit into believing that \nwe couldn't trust out own gender feels. So it makes sense."+
"\nRT @CodeWitchJen: I mean as kids we were \nconstantly gaslit into believing that \nwe couldn't trust out own gender feels. So it makes sense."+
"\nRT @CodeWitchJen: I mean as kids we were \nconstantly gaslit into believing that \nwe couldn't trust out own gender feels. So it makes sense."+
"\nWe can all agree that Jim Halpert was \na monster person who routinely gaslit \nneuroatypical people, right?"+
"RT @CodeWitchJen: I mean as kids we were \nconstantly gaslit into believing that \nwe couldn't trust out own gender feels. So it makes sense.",30,30);

textFont(cuckFont);textSize(17);
fill(0);textAlign(CENTER,CENTER);text("\n\n\n\n\nThe Girl Who Was Gaslit Into Killing Herself #TheGirlWho"
,width/2,height/2-95);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nI mean as kids we were constantly gaslit into believing that \nwe couldn't trust out own gender feels. \nSo it makes sense."+
"\n@carmenmmachado I feel gaslit by \nwhat a terrible writer Ryan Murphy is, \nlike why are we acting like everything is ok"+
"\nI’m here for a time when Millennials, \na generation gaslit into thinking its troubles are \n100% self-derived, reject that narrative."+
"\nRT @TenementHouse11: Introducing the history of \n#spiritualism in our gaslit \n#parlour 🔮#spiritsofthetenements h\nttps://t.co/bBm3MRewz4"+
"\nRT @TenementHouse11: Introducing the history of \n#spiritualism in our gaslit \n#parlour 🔮#spiritsofthetenements h\nttps://t.co/bBm3MRewz4"+
"\nIntroducing the history of \n#spiritualism in our gaslit #parlour \n🔮#spiritsofthetenements https://t.co/bBm3MRewz4",width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 21:10:12",width/2+270,height/2+300);

pdf.nextPage();//p16
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nBook your spot in classes at Gaslight Creations in the \nClifton Gaslight Business District. \nhttps://t.co/zBjmlTYU7L"+
"\nCould there ever be a current movie \nwhose title  defines a sinister \nbehavior as perfectly as #Gaslight?"+
"\nRT @FemalesLikeUs: Yes TransActivists use \nnarcissistic psychological abuse to \nGASLIGHT the entire FEMALE \npopulation into submission. https:…"+
"\nMe: will gaslight myself into thinking \nI'm not sick and then swallow/cough/etc"+
"\nWe're kick starting #Halloween with some picks of \nour favourite #spooky films! our choice for today: \n#Gaslight 1940 https://t.co/auZUn6khwL",30,30);

textFont(cuckFont);textSize(19);
fill(0);textAlign(CENTER,CENTER);text("\nWomen experience it daily from males. \nMale-trans use Narcissistic Abuse Tactics to \ngaslight Women into complying w/… \nhttps://t.co/vEnJzSdtQH"
,width/2,height/2-86);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\n@NeverMoreBUSH Thy can't gaslight her. @JoyAnnReid \n@supraTruth @GomezReporting @Trippn21"+
"\nRT @gaslightstjames: #Taxi #Taxicabs \n#Cabbies £30 commission per full \npaying customer at Gaslight of St James"+
"\nGaslight Theatre presents \nBad Seed at Gaslight Theatre \nstarts in 15 minutes!"+
"\n@Eliot_Collie and yet, \nplenty of white americans will \ngaslight the fuck out of these native people."+
"\n45 by Gaslight Anthem"+
"\nYou're with fam. \nLots of people are Voting Jill Stein!"+
"\nDon't let Blackout Gaslight you! \nLook at their empty Rallie… https://t.co/2jl4iR1XZA"+
"\nWe are so honored to be recognized in the"+ "\"Best of Tucson 2016\"" +"\nfor the below categories! #supportlocalbusiness... \nhttps://t.co/XBSyPMifUi"
,width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:10/27/2016 21:13:24",width/2+270,height/2+300);

pdf.nextPage();//17
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nJP is such a slimy thug with UNBEELIEVABLE privilege issues and \na tendency to gaslight. \nWhy the FUCK is he still on the show??? #MiC"+
"\nYou're not a real college student until u have no clean clothes, \nur gaslight is on, you have -$5, & \nare living on expired lunch meat & wine"+
"\n@redshira I'm so, so sorry. \nI was in this spot the last two years after \nbeing gaslight and denied in 2014. \nYou deserve so much better.",30,30);

textFont(cuckFont);textSize(19);
fill(0);textAlign(CENTER,CENTER);text("@LauriebrettX @garyaknichols \neveryone at #gaslight is in awe of \nhow much you have raised \n#welldone #GoSoberUK #Macmillan #cancerresearch"
,width/2,height/2-120);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nRT @CandlelightRed: Hey! We've got some new shows booked! Ya comin'???"+
"\nWednesday, November 23 - The Gaslight in Wellsboro, PA... https://…"+
"\nHey! We've got some new shows booked! Ya comin'??? "+
"\nWednesday, November 23 - The Gaslight in Wellsboro, PA... https://t.co/agtqyrLQRt"+
"\nRT @Tampa_Live: RT @CityofTampa Don't forget! \nThe Mayor's Food Truck Fiesta returns \nTOMORROW from 11am-2pm in Lykes Gaslight... https://t.c…"+
"\n.@walterolson"+
"\nMeanwhile, our own govt is attempting \nto gaslight the American people into \nbelieving this is something other than terrorism."+
"\nRT @bebebebender: The media gaslight is a hell of a thing. @d_seaman"+
"\nRT @bebebebender: The media gaslight is a hell of a thing. @d_seaman"+
"\nThe Gaslight Anthem - The '59 Sound (Acoustic) \nhttps://t.co/hkFrFYLn20 via @YouTube",width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:11/01/2016 16:57:30",width/2+270,height/2+300);

pdf.nextPage();//18

textFont(cuckFont);textSize(20);
fill(0);textAlign(CENTER,CENTER);text("\n@VanPlague i can only dream of the gaslightings \nthat must have taken place... the email leaks... ahh the times"+
"\n@euorleans gaslightings à parte, o seu dia está muito louco"+
"\n\nSTOLEN:11/01/2016 16:59:29"
,width/2,height/2-100);

pdf.nextPage();//19
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,RIGHT);
text("\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…"+
"\nRT @d_seaman: Fucking gaslighter. Your husband raped multiple women \nwhile in a position of power over them."+
"\nEnjoy prison. You’ve shown us…",30,30);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:11/01/2016 16:59:42",width/2+270,height/2+300);

pdf.nextPage();//20
textFont(tweetFont);
fill(255, 54, 28);textAlign(LEFT,LEFT);
text("\nRT @zackfox: when you see a bright future then realize \nthe brightness is from all your bridge burning, \ngaslighting, and fighting fire with…"+
"\nRT @zackfox: when you see a bright future then realize \nthe brightness is from all your bridge burning, \ngaslighting, and fighting fire with…"+
"\nRT @zackfox: when you see a bright future then realize \nthe brightness is from all your bridge burning, \ngaslighting, and fighting fire with…"+
"\nRT @zackfox: when you see a bright future then realize \nthe brightness is from all your bridge burning, \ngaslighting, and fighting fire with…"+
"\nRT @zackfox: when you see a bright future then realize \nthe brightness is from all your bridge burning, \ngaslighting, and fighting fire with…",30,30);

textFont(cuckFont);textSize(19);
fill(0);textAlign(CENTER,CENTER);text("\n#Gaslighting https://t.co/zL2VbGnePw"+
"\nWhen authority figures like #doctors tell you \nyr experience isn't real, over and \nover again, that is called #gaslighting. It's abuse"
,width/2,height/2-90);

textFont(tweetFont);
fill(255, 54, 28);textAlign(RIGHT,RIGHT);
text("\nRT @zackfox: when you see a bright future then realize \nthe brightness is from all your bridge burning, \ngaslighting, and fighting fire with…"+
"\nRT @zackfox: when you see a bright future then realize \nthe brightness is from all your bridge burning, \ngaslighting, and fighting fire with…"+
"\nRT @zackfox: when you see a bright future then realize \nthe brightness is from all your bridge burning, \ngaslighting, and fighting fire with…"
,width/2+270,height/2);
textFont(timeFont);textSize(10);fill(0);textAlign(RIGHT,RIGHT);text("\n\nSTOLEN:11/01/2016 16:58:41",width/2+270,height/2+300);

pdf.nextPage();//p21
exit();

}
//endRecord();

image image image image image image image image

Written by Comments Off on tigop-Book Posted in Book

Anson-Book

Here’s the full Spirit Animals PDF: SpiritAnimals_print.pdf

From the Artist’s Note at the beginning of the book:

“Spirit Animals is a computationally generated book of dances, created with Processing 3, an open-source software for creative coding. The dances can be done solo or in groups, with or without music, and are meant for novices and professionals alike. They aim, though do not promise, to be physically possible.

Spirit Animals draws influence from the Fluxus art movement, John Cage’s compositions, Andy Warhol’s Dance Diagrams, and traditional instructional dance diagrams such as the Fox Trot, Tango, and Lindy Hop. The dance names are derived using a random generator combining an adjective and an animal, producing variations on the “Funky Chicken.”

I hope you’ll use Spirit Animals to dance anywhere, at any time, and with anyone. Why walk, when you can dance?”

Spirit Animals marks a new step for me towards creating participatory computational artworks and experiences. I’ve been reading and thinking a lot about the ideas of participation, interaction, and the moment of encounter. I’m very interested in artworks which create situations for people to interact in some physical manner with themselves, the world, and others around them. I’m reading about relational aesthetics, “emancipated spectatorship” and the writings of Claire Bishop, Nicholas Bourriard, Roland Barthes, and Umberto Eco. Conceptually, Spirit Animals is hopefully fun, silly, and has a low barrier to entry for anyone to pick up the book and play. I like that this book/PDF format is easily distributable, and doesn’t require any technology to “perform.” Spirit Animals is also a visual experiment in computationally generated movement patterns. Secretly/not so secretly I have a wild desire to be a choreographer – and have a long history of collaborating with dancers and performers. However, not being a dancer, I’m a bit hopeless at actually creating the choreography, so I enjoy the “computer as collaborator” aspect of the dance creation here. I’m not sure where this goes, but I’m interested in exploring these ideas further.

In terms of the technical aspects of creating this book – let me count the ways….. First of all, this is by far the most complex and longest program I’ve ever written. Still being a beginner with coding and Processing, this was actually a mammoth task. I’ve learned a lot, and feel that the biggest technical skill I worked on here was just graphical – generating the foot patterns and figuring out the layout that made the most sense, and making those pesky curved arrows go where they should. I definitely didn’t work with Basil.js or InDesign but did put some of the text pages together in Illustrator. Rita.js is interesting to me, but will have to be tackled at a later date.

Here are some images from the book:

Here is the code:

// Processing 3.0x code
import processing.pdf.*;

float ax;
float ay;
float bx;
float by;
float cx;
float cy;
float dx;
float dy;

float f;
float g; 

float px;
float py;
float qx;
float qy;

int pageWidth = 72 * 8;
int pageHeight = 72 * 10;

float cellMarginX;
float cellMarginY;

int rightFoot_col = 4;
int rightFoot_row = 7;
int leftFoot_col = 4;
int leftFoot_row = 7;

final int pixelsPerInch = 72;

int nCols = 5;
int nRows = 7;
int nSteps = 7;

float cellSpaceW = pageWidth / nCols;
float cellSpaceH = pageHeight / nRows;

final int gridLineWidth = 3;

IntList usedCells;

PShape leftFoot;
PShape rightFoot;

float footScale = 0.07;
float gridMargin;
float gridWidth;
float gridHeight;
float cellWidth, cellHeight;

float leftFootWidth;
float leftFootHeight;
float rightFootWidth;
float rightFootHeight;

PFont myFont;
PFont myTitles;

int currentCell = (nRows * nCols) /2;

int cellNumber; 

int cellCol = cellNumber % nCols;
int cellRow = cellNumber / nCols;

StringList pageTitles;


//------------------------------


void setup() {
  size(612, 792); // 8.5 x 11  pixelsPerInch * 8.5 and PixelsPerInch * 11

  beginRecord(PDF, "spiritanimals174.pdf");
  //PGraphicsPDF pdf = (PGraphicsPDF) g;

  leftFoot = loadShape("foot_L.svg"); 
  rightFoot = loadShape("foot_R.svg"); 

  leftFootWidth = leftFoot.width * footScale;
  leftFootHeight = leftFoot.height * footScale;
  rightFootWidth = rightFoot.width * footScale;
  rightFootHeight = rightFoot.height * footScale;

  // indent the grid by a half inch all around
  gridMargin = pixelsPerInch * 0.90;

  gridWidth = 612 - (gridMargin * 2);
  gridHeight = 792 - (gridMargin * 2);

  cellWidth = gridWidth / nCols;
  cellHeight = gridHeight / nRows;

  myFont = createFont("Helvetica", 15, true); 
  textFont(myFont); 

  myTitles = createFont ("Helvetica ", 40);
  //textFont(myTitles);

  usedCells = new IntList();

  createPageTitles();

  noLoop();
}


//---------------------------------------------------------
void draw() 
{

  background(255); // white
  drawDanceFloor();


  // dance some steps!
  usedCells.append(currentCell);

  for (int i = 0; i < nSteps; i++) 
  {
    drawLeftFootInCell(currentCell, i+1);

    int nextCell = getPossibleNextCell();
    while (usedCells.hasValue(nextCell)) 
    {
      nextCell = getPossibleNextCell();
    }

    drawLineFromCellToCell(currentCell, nextCell, i);

    currentCell = nextCell;
    usedCells.append(currentCell);

    drawRightFootInCell(currentCell, i+1);   
    nextCell = getPossibleNextCell();
    while (usedCells.hasValue(nextCell)) 
    {
      nextCell = getPossibleNextCell();
    }

    if (i < nSteps - 1) 
    { 
      stroke (0, 100, 255, 100);
      strokeWeight(2); 
      drawLineFromCellToCell(currentCell, nextCell, 1);

      currentCell = nextCell;
      usedCells.append(currentCell);
    }
  }
  textAlign(CENTER);
  fill(0);
  textFont(myTitles);
  text(pageTitles.get(0), 306, 65);
  println(usedCells);

  //textFont(myTitles, 75);
  fill(0);

  endRecord();
}

//---------------------------------------------------------
void drawLeftFootInCell(int cellNumber, int numberToDraw)
{
  int cellCol = cellNumber % nCols;
  int cellRow = cellNumber / nCols;

  float drawLX = (cellCol * cellWidth) + (cellWidth / 2) - (leftFootWidth / 2) + gridMargin;
  float drawLY = (cellRow * cellHeight) +  (cellHeight / 2) -  (leftFootHeight / 2) + gridMargin;

  shape(leftFoot, drawLX, drawLY, leftFootWidth, leftFootHeight);
  fill(0); 
  text (numberToDraw, drawLX - 10, drawLY + 40);
}

//---------------------------------------------------------
void drawRightFootInCell(int cellNumber, int numberToDraw)
{
  int cellCol = cellNumber % nCols;
  int cellRow = cellNumber / nCols;

  float drawRX = (cellCol * cellWidth) + (cellWidth / 2) - (rightFootWidth / 2) + gridMargin;
  float drawRY = (cellRow * cellHeight) +  (cellHeight / 2) -  (rightFootHeight / 2) + gridMargin;

  shape(rightFoot, drawRX, drawRY, leftFootWidth, leftFootHeight);
  fill(0); 
  text (numberToDraw, drawRX - 10, drawRY + 40);
}

//---------------------------------------------------------
void drawLineFromCellToCell(int fromCell, int toCell, int whichStep)
{
  float fromCellX = topOfCellXCoordinate(fromCell); // ax
  float fromCellY = topOfCellYCoordinate(fromCell); // ay
  float toCellX = topOfCellXCoordinate(toCell); // dx
  float toCellY = topOfCellYCoordinate(toCell); //dy

  float fromCellX_qx = lerp(fromCellX, toCellX, 0.3333); // px
  float fromCellY_qy = lerp(fromCellY, toCellY, 0.3333); // py
  float toCellX_px = lerp(fromCellX, toCellX, 0.6666); // qx
  float toCellY_py = lerp(fromCellY, toCellY, 0.6666); // qy

  float tx = toCellX-fromCellX;
  float ty = toCellY-fromCellY;
  float th = sqrt((tx*tx) + (ty*ty));


  f = 0.13; 
  g = 0.11; 
  if (whichStep%2 == 1) {
    f = 0-f;
  }
  if (whichStep%2 == 1) {
    g = 0-g;
  }
  if (th < cellSpaceW) { f = g = 0; } println("Hey: " + whichStep + " " + th + " " + cellSpaceW); ax = fromCellX; ay = fromCellY; bx = fromCellX_qx - f*ty; by = fromCellY_qy + f*tx; cx = toCellX_px - g*ty; cy = toCellY_py + g*tx; dx = toCellX; dy = toCellY; // direction-sensitive offsets to the line coordinates float offsetx = 15; float offsety = 30; if (tx > 0) { // then I'm going from Left to Right
    ax += offsetx; 
    dx -= offsetx;
  } else if (tx < 0) { // then I'm going from right to left ax -= offsetx; dx += offsetx; } else { // tx == 0 if (ty > 0) { // going down
      ay += offsety;
      dy -= offsety;
    } else if (ty < 0) { // going up ay -= offsety; dy += offsety; } } float sepY = 5; if (ty > 0) { // going down
    ay += sepY;
    dy -= sepY;
  } else if (ty < 0) { // going up ay -= sepY; dy += sepY; } boolean bDrawColoredEllipses = false; if (bDrawColoredEllipses) { float eR = 7; noStroke(); //stroke(0,0,0); strokeWeight(1); fill(255, 0, 0); // A = red ellipse(ax, ay, eR, eR); fill(0, 255, 0); // B = green ellipse(bx, by, eR, eR); fill(0, 0, 255); // C = blue ellipse(cx, cy, eR, eR); fill(255, 255, 0); // D = yellow ellipse(dx, dy, eR, eR); } /* fill(255, 200, 255); // Q = light pink ellipse(fromCellX_qx, fromCellY_qy, 10, 10); fill(100, 100, 255); // P = dark purple ellipse(toCellX_px, toCellY_py, 10, 10); */ // draw the curve noFill(); stroke(255); // white strokeWeight(4); // thick beginShape(); curveVertex(ax, ay); curveVertex(ax, ay); curveVertex(bx, by); curveVertex(cx, cy); curveVertex(dx, dy); curveVertex(dx, dy); endShape(); fill(0); noStroke(); ellipse(ax, ay, 5, 5); noFill(); stroke(0, 0, 0); // black strokeWeight(1); // thin beginShape(); curveVertex(ax, ay); curveVertex(ax, ay); curveVertex(bx, by); curveVertex(cx, cy); curveVertex(dx, dy); curveVertex(dx, dy); endShape(); drawArrowhead(cx, cy, dx, dy); //line(fromCellX, fromCellY, toCellX, toCellY); } //--------------------------------------------------------- float topOfCellXCoordinate (int cellNumber) { int cellCol = cellNumber % nCols; return (cellCol * cellWidth) + (cellWidth / 2) + gridMargin; } //--------------------------------------------------------- float topOfCellYCoordinate (int cellNumber) { int cellRow = cellNumber / nCols; return ((cellRow * cellHeight) + (cellHeight / 2)) + gridMargin; } //--------------------------------------------------------- int getPossibleNextCell() { // both moveX and moveY cannot be zero - int cellCol = currentCell % nCols; int cellRow = currentCell / nCols; int moveX = (round(random(-2, 2))); while ((moveX + cellCol >= nCols) | (moveX + cellCol < 0)) { moveX = (round(random(-2, 2))); } int moveY = (round(random(-2, 2))); while ((moveY + cellRow >= nRows -1) | (moveY + cellRow < 0)) 
  {
    moveY= (round(random(-2, 2)));
  }

  if (moveX == 0 && moveY == 0) 
  {
    // arbitrary
    if (cellCol <= 5) {
      moveX = 3;
    } else {
      moveX -= 2;
    }
  }

  cellCol = cellCol + moveX;
  cellRow = cellRow + moveY;

  return cellRow * nCols + cellCol;
}

//---------------------------------------------------------

void drawArrowhead(float fromCellX, float fromCellY, float fromCellX_halfway, float fromCellY_halfway) {
  float hx = fromCellX_halfway - fromCellX; 
  float hy = fromCellY_halfway - fromCellY; 

  float len = dist(fromCellX, fromCellY, fromCellX_halfway, fromCellY_halfway);
  float dh = sqrt(hx*hx + hy*hy); //same!
  float ang = atan2 (hy, hx); // hY first!! just is. 
  if (abs(hx) < 0.0001) {
    ang = 0-ang;
  }

  float arrowSize = constrain(len/4, 10, 20); 
  pushMatrix(); 
  translate(fromCellX_halfway, fromCellY_halfway);
  rotate(ang + radians(70)); 
  line(0, 0, 0, arrowSize); 
  popMatrix(); 
  pushMatrix(); 
  translate(fromCellX_halfway, fromCellY_halfway);
  rotate(ang - radians(70)); 
  line(0, 0, 0, -arrowSize); 
  popMatrix();
}



//--------------------------------------------------------

void drawDanceFloor()
{
  stroke(255, 255, 255, 30); 
  strokeWeight(gridLineWidth); 

  // draw horizontal lines
  // number of lines  draw are (nRows + 1)

  // set initial draw point
  float drawX = gridMargin;
  float drawY = gridMargin;

  println(cellHeight);

  for (int row = 0; row <= (nRows + 1); row++) {
    line(drawX, drawY, drawX + gridWidth, drawY);
    // bump drawY down to next row
    drawY += cellHeight;
  }

  // draw vertical lines

  // set initial draw point
  drawX = gridMargin;
  drawY = gridMargin;

  for (int col = 0; col <= (nCols + 1); col++) {
    line(drawX, drawY, drawX, drawY + gridHeight);
    // bump drawY down to next row
    drawX += cellWidth;
  }
}

// animals and adjectives collected at random from https://github.com/dariusk/corpora/ with 7 numbers generated by random.org integer generator
void createPageTitles()
{
  StringList myAnimals = new StringList(new String[]{"cougar", "elk", "skunk", "tapir", "whale", "wolf", "newt"});
  StringList myAdjectives = new StringList(new String[]{"vaulting", "sparing", "one-eyed", "gleaming", "doctrinal", "designing", "brimstone"});

  pageTitles = new StringList();
  for (int k=0; k < nSteps; k++)
  {
    String animal;
    String adjective;
    if (myAnimals.size() == 1)
    {
      animal = myAnimals.get(0);
      adjective = myAdjectives.get(0);
    } else
    {
      int randomAnimal = (int)random(myAnimals.size());
      animal = myAnimals.get(randomAnimal);
      myAnimals.remove(randomAnimal);
      int randomAdjective = (int)random(myAdjectives.size());
      adjective = myAdjectives.get(randomAdjective);
      myAdjectives.remove(randomAdjective);
    }

    pageTitles.append(adjective + " " + animal);
  }

  println(pageTitles);
}
Written by Comments Off on Anson-Book Posted in Book

Zarard-Book

PDF Download of The Faces of Teenie Harris

Teenie Harris was a leading African-American photographer and photojournalist, who was active in Pittsburgh from the ~1940s-1970s. Through my internship at the Carnegie Museum of Art, I’ve been lucky to have access to the museum’s large database of Teenie’s photographs.

This book is catalog of all the faces in a (small but representative) sample of 2000 photos from the photographer Teenie Harris. I used the dataset of photos and a JSON output of a facial detector to isolate all the faces in each image; compute the brightness of each face photo; and sort the faces by brightness. The effect was a book that goes from dark to light, eventually fading into the background color of the page.

Most of my process was oriented around creating the code to generate the book. At first I needed to figure out how to retrieve all of the pixels that contained the face, so i used the bounding box that the face detector’s JSON output gave me. Then for each set of pixels I created a face object with the original size, the pixels, and the brightness. When creating the new face object I had to resize each face to a standard 20 pixels by 20 pixels. Then I sorted each face object by brightness. Then I looped through the sorted faces to create grids of faces that looks like this:

screen-shot-2016-10-31-at-12-27-44-pm

What I discovered was that the charm of the book wasn’t really the gradient but all of the variation within the gradient: the hidden pockets of light and shadow, the variation in face orientation, the expressions that deviate from the traditional portrait smile, and the occasional (and very rare), misclassified “face”. My inspiration for doing stemmed from the practice of of african americans lightening their photos to look “light-skin” or “passing white”. At first I wanted to see if I could catch any hint of that through sorting the faces, but then I realized i had no way of knowing the actual skin tones of the people in the photo versus the exposure that he used.

All things considered, I think this was a successful project. If i were to change anything, i’d probably make the book double-sided instead of flip book style. Additionally I would run the script on the full dataset of images (which i did not have at the time) instead of a sample. Also I struggled a lot with sorting because I couldn’t figure out how to bring in built in java libraries, and so i implemented quicksort which gave me a lot of bugs, so in retrospect I would’ve just asked someone for help sooner.

 

 

Written by Comments Off on Zarard-Book Posted in Book

Aliot-Book

My book is called “What I Was Doing When You Texted Me.” It’s a personal text-photo memoir of the last two years of my life and also a look-up table for texts that my friends have sent me. It’s very personal project so my opinion is very biased, but I thought the content and the juxtaposition of certain texts and photos were very compelling. Because of the sheer amount of data I had produced, there were upwards of 20 photos/texts that were received/taken within one second of each other, so the “what I was doing when you texted me” question is very literally and accurately answered. The answer was “I was looking at this thing.” (Unsurprisingly many of the photos were screenshots, photos of my boyfriend, or selfies).

In order to generate my book, I cross referenced the time stamps of all of my photos using the EXIF metadata with the time stamps to all of my received texts. After scraping this data, I used processing to create a CSV containing all of the texts received with the photo with the closest time stamp. After ordering the data according to temporal proximity, it was easy to compile a book showcasing the information.

What I Was Doing When You Texted Me (PDF)

Table data;
Table final_data;
Table final_no_repeats;
StringList used_pics;

void setup(){
  final_data = new Table();
  final_data.addColumn("pic_file");
  final_data.addColumn("pic_time");
  final_data.addColumn("text_time");
  final_data.addColumn("text");
  final_data.addColumn("to");
  final_data.addColumn("from");

  data = loadTable("combined_pic_text.csv", "header");
  
  for (int i = 0; i<500; i++){
    for (TableRow row: data.rows()){
      if (int(row.getString("dif")) == i) {
        println(row.getString("dif"));
        String file = row.getString("pic_file");
        String pic_time = row.getString("pic_date_time");
        String text_time = row.getString("text_date_time");
        String text = row.getString("text");
        String to = row.getString("to");
        String from = row.getString("from");
        String dif = row.getString("dif");      
        
        TableRow newRow = final_data.addRow();
        newRow.setString("pic_file", file);
        newRow.setString("pic_time", pic_time);
        newRow.setString("text_time", text_time);
        newRow.setString("text",text);
        newRow.setString("from", to);
        newRow.setString("to",from);
        newRow.setString("dif", dif);
        
        saveTable(data, "data/data_sorted.csv");
      }
    }
  }
  
  saveTable(final_data, "data/data_sorted.csv");
  
  remove_duplicates();
}

void remove_duplicates(){
  final_no_repeats = new Table();
  final_no_repeats.addColumn("pic_file");
  final_no_repeats.addColumn("pic_time");
  final_no_repeats.addColumn("text_time");
  final_no_repeats.addColumn("text");
  final_no_repeats.addColumn("to");
  final_no_repeats.addColumn("from");
  final_no_repeats.addColumn("dif");

  for (TableRow row: final_data.rows()){
    String file = row.getString("pic_file");
    if (!used_pics.hasValue(file)){
      used_pics.append(file);
      TableRow newRow = final_no_repeats.addRow();
      newRow.setString("pic_file", row.getString("pic_file"));
      newRow.setString("pic_time",  row.getString("pic_time"));
      newRow.setString("text_time",  row.getString("text_time"));
      newRow.setString("text", row.getString("text"));
      newRow.setString("from",  row.getString("from"));
      newRow.setString("to", row.getString("to"));
      newRow.setString("dif",  row.getString("dif"));
    }
  }
}
Written by Comments Off on Aliot-Book Posted in Book

cambu-book

the-book

Description

This small square book tells the story of my summer in Los Angeles in 2014. After sophomore year of high school, I flew from Toronto, ON to live with extended family in LA while working at a small ‘startup’ company. The data to plot these moments of my summer was retrieved from Google’s location tracking database (https://myactivity.google.com) and chosen in a semi-computational manner that involved a non-perfect human input system.

Process

Going into this project, I knew I wanted to work with maps and location data. I’d been playing around with Mapbox Studio and I wanted an excuse to really dig into it. The next ingredient was to use my personal location data from Google My Activity — this made it easy for me to export all of my location data as a massive JSON blob. My initial idea revolved around using that data in conjunction with some longitude/latitude math to trace a line through the earth and create a book that had my location(s) and the projected location(s) of a doppelganger version of me in a different city.

I decided against this direction in the end because I wasn’t convinced there was any particular meaning to tracing a direct (or even canted/distorted line) through the planet. In the end, I decided to focus more on the exploration of my own data instead of trying to make artificial juxtapositions. See the below image for more process of the project.

process

Self-Evaluation

For me personally, looking at the book has a lot of meaning and feels very ‘deja vu’-ish because I can viscerally remember being at all of the highlighted locations. It also has a deep ‘uncanny valley’ feeling because, of course, the images are from Google street view and not things I personally captured — but, I can imagine having captured something similar. After all, I was there!

That said, something I didn’t account for when starting with the idea of using my personal location data was that it wouldn’t have the same meaning to other people. All of the psychological triggers that are working on me just aren’t being experienced the same way by other people. If I were to iterate further on this concept, I would create a fully automated pipeline for taking anyone’s location data + selected date range and converting that into their own book. I think then people would be able to feel the same way I did about my book.

Video

Here’s a video of Professor Levin flipping through my book:

Full Book

Code

python node transformer app:

import json
from datetime import datetime as dt

# JSON LOAD
myFile = open("LocationHistory.json")
# myFile = open("testLocHist.json")
js = json.load(myFile)
#js = [2012,2013,2014,2015,2016,2017]

# Vars 
nLocations = len(js["locations"])
deleteIndexes = set() #must be a set so it be be easily indexed, gautam bose (andrew: gbose) helped me with the logic behind switching this from a list to a set

print("years in main loop: ")
for i in range(nLocations):
    curTimeStamp = js["locations"][i]["timestampMs"]
    #print(curTimeStamp)
    ## converting milliseconds to seconds   
    humanDate = dt.fromtimestamp(float(curTimeStamp)/1000.0)
    #print(humanDate)
    curYear = humanDate.year
    curMon = humanDate.month 
    curDay = humanDate.day

    #print(str(curMon) + " " + str(curDay))

    if curYear == 2014:  # if the condition is met, the items will be deleted
        # keep these
        # print(curYear)
        if curMon >= 7 and curMon <= 8: #print(curYear) if curMon == 7: if curDay >= 12:
                    pass
                    #print("locked")
                    #print(humanDate)
                else:
                    deleteIndexes.add(i)

            if curMon == 8:
                if curDay <= 18:
                    pass
                    #print("locked")
                    #print(humanDate)
                else:
                    deleteIndexes.add(i)
        else:
            deleteIndexes.add(i)
    else: 
        deleteIndexes.add(i)
    #@Cam added this conditional to remove a brief trip to SF

    if js["locations"][i]["latitudeE7"] < 350000000: pass else: # print('case1') deleteIndexes.add(i) # print("## start delete loop from ##") # print(deleteIndexes) # print("w/ LENGTH OF ---> ")
# print(len(deleteIndexes))
# print("______")



### PERCENTAGE GUESSOR 
# print(nLocations)
# print("&&")
# print(deleteIndexes)

for x in range(nLocations, 0, -1):
    print(x)
    if x in deleteIndexes:

        del js["locations"][x]

##### CHECKER LOOP
# for y in range(len(js["locations"])):
#     curAgTimeStamp = js["locations"][y]["timestampMs"]
#     # print(curAgTimeStamp)

#     print(dt.fromtimestamp(float(curAgTimeStamp)/1000.0))


open("updated-file.json", "w").write(
    json.dumps(js, sort_keys=True, indent=4, separators=(',', ': '))
    )



# print(deleteIndexes)
# print(js["locations"])
# date = {}

# date = datetime.fromtimestamp(int("unixTimeVar"))
# year = date.year
# hour = date.hou∫∫r∫

##date.year
##etc. unpack 
# #print("a total of" + nLocations + "exist in this JSON File") #TypeError: Can't convert 'int' object to str implicitly
# print("Locs:")
# print(nLocations)
#print(curYear)
#print(huma  nDate.year)ss
# year = humanDate.year
# print(js["locations"][0]["timestampMs"])
# print(js["locations"][1]["timestampMs"])

javascript web app to call APIs and download images to local directory:

A Simple Map












 

 




//Load JSON File
var locations = [] // all data

window.onload = function rdy() {
    newMapImage(34.1187624,-118.2751063,12,640,640)
    newStreetImage(34.1187624,-118.2751063,640,640, 90, 235, 10)
  }

var i = 0;
function again(jumpVal, newHeading) {
    var longitude = locations[i][0];
    var latitude = locations[i][1];
    var zoom = 12 //locations[0][2]
    var width = 640;
    var height = 640;
    var fov = 90;
    var S = null;
    var pitch = 10;

    if (newHeading == null) {
        var heading = 235;
    } else {
        var heading = newHeading;
    }

    if (S == null) {
      newMapImage(longitude,latitude,zoom, width, height, 0);
      newStreetImage(longitude,latitude, width, height, fov, heading, pitch, 0);
    } else {
      newMapImage(longitude,latitude,zoom, width, height, 1);
      newStreetImage(longitude,latitude, width, height, fov, heading, pitch, 1);
    }



    console.log(longitude + " " + latitude)
    console.log(i + " / " + locations.length)
    //setTimeout(function(){alert(i)}, 500);
    // newMapImage(longitude,latitude,zoom, width, height, save);
    // newStreetImage(longitude,latitude, width, height, fov, heading, pitch, save);
    i = i + jumpVal;


}

function newMapImage(long, lat, zoom, width, height, save) { // map image
  var linkMap = "https://api.mapbox.com/styles/v1/mapbox/streets-v9/static/" + lat + "," + long + "," + zoom + "/" + width + "x" + height + "?access_token=pk.eyJ1Ijoic3VwZXJjZ2VlayIsImEiOiJjaWZxMzV6NnFhb3pjaXVseDQ1dm84Z2RkIn0.T5qZqiB_JanRezs012Zppw";
  document.getElementById('myMap_image').src = linkMap;
  document.getElementById('myMap_link').href = linkMap;
  // document.getElementById('myMap_link').download = 1
  if (save == 1){document.getElementById('myStreetView_link').click();}
}

function newStreetImage(long, lat, width, height, fov, heading, pitch, save) { // street image
    var link = "https://maps.googleapis.com/maps/api/streetview?size=" + width + "x" + height + "&location=" + long + "," + lat + "&fov=" + fov + "&heading=" + heading + "&pitch=" + pitch + "&key=AIzaSyAAhrTirgQBQJH88rpw6LpOfp3oMRTMzqg";
    document.getElementById('myStreetView_image').src = link;
    document.getElementById('myStreetView_link').href = link;
    // document.getElementById('plugBoi2').download = 1
    if (save == 1){document.getElementById('myStreetView_link').click();}
}



map




street






Written by Comments Off on cambu-book Posted in Book

kadoin-Book

Poetry for Robots by Robots

cover

By combining some Shakespeare and computer user guide manuals in a Markov chain generator, the pages on the left of this book are a series of rhyming couplets that don’t always make much sense, but every once in a while says just the right thing to make that special robot swoon. The pages on the right, when flipped through quickly, show a handsome bot falling in love.

Sees the world through rose tinted receivers and transmits the waveforms of the words "I love you." Robot sees the world through rose tinted receivers and transmits the waveforms of the words “I love you.”

PDF of book here:

botbook PDF

botBook Code

Written by Comments Off on kadoin-Book Posted in Book

Drewch – Book

Inspired by The Tower of Babel, I decided to make a stream of letters, some more legible than others, that make out a poem of sorts that I wrote. Can this really mean anything, since the legible letters are still part of this incoherent stream? Can you actually know what this poem means?

binder2-1binder2-3binder2-2

Processing code (at Github): https://github.com/AndyChanglee/60-212/tree/readme-edits/banner

The book as a PDF: ./wp-content/uploads/2016/10/drewch_book.pdf (48 MB)

P.S. I’ve had bad run-ins with making books, especially with inDesign and Adobe products in general. This project was really taxing and I ended up not making a physical book because it was too expensive.

Written by Comments Off on Drewch – Book Posted in Book

Guodu-Book

Alphabet Fonts 

hold hands open

 

dsc02378_1

dsc02434

PDF Version

Final: AlphabetFonts.pdf

My book is about introducing fonts alphabetically. Start your baby early 🙂

Here’s a video of Golan flipping through my book.

On the right page there are 3 randomly generated alphabetical letters of different fonts and on the left is the names of those fonts. I thought it would be interesting to not make it extremely apparent which font matched with each letter so the letters are randomly placed with a slight change in opacity and either being white, transparent white, or transparent black. In this way, I hoped that the difficulty of matching the letters would prompt people to observe the letters more closely and see the subtle or extreme differences.

My own experience to getting into typography and the nuanced differences between fonts and their history was through examining fonts up close. I would print the same letter but of different fonts at 600+ pt on paper because it was easier to trace and note their differences when the fonts are so big.

example Things to observe and learn when you start becoming a type nerd

Inspiration

I became really interested in how to better display the randomization of my font library after Marius Watz‘s awesome demo on Basil.js for scripting InDesign. I began searching baby alphabet books and was inspired by the illustration style of Anna Kövecses, a Hungarian graphic designer. Anna also made the alphabet book of 44 Hungarian letters (pictured below) for her 4-year old daughter.

il_570xn-754715927_qqvq Baby Alphabet Book anna-kovecses_hungarian-alphabet Anna Kovecses’s Hungarian Alphabet Book

Process + Sketches

20161029_145747 Initial Ideas for an ABC book 20161029_150208 Hand Binding Notes draft Experimenting with the appearance, opacity, and number of letters process Hand Binding in Progress

 

Self Evaluation/Future Iteration

  • I would like to make the fonts actually be presented alphabetically. For instance the letter A should have 3 different types of fonts starting with A, like Avenir, Arial, and American Typewriter. I ran into a small problem of actually not having a few fonts starting with certain letters or just not having at least 3 (J, Q,  U, V, X, Y). I’m going to need to download some fonts to make this work…but going to make this happen! I regret that I didn’t figure out how to make this happen for this iteration.
  • Definitely going to have an outside printing service print my book (Espresso, Blurb etc). While it was fun learning how to hand bind and having control over the quality of paper and color, it was definitely laborious and I think even contradictory to the limitless/endless iterative quality of generative books.
  • I still want to explore the composition and placement of the letters of the right page. Right now I have random placement of letters / colors = white, transparent, and transparent black. I’m not convinced this is the best way to represent the letters because there’s not a whole lot of meaning behind it other than it looks pretty. While the random placement produces some interesting compositions, sometimes it is really off.
  • For the left page, I am thinking of adding more text like what does the font look like from A –> Z instead of just naming the font. Use Rita.js or Temboo…what about a simple sentence or phrase where every word of it starts with the letter (An Ant, Big Bunny, Crazy Corn etc) or just #ahhhhh #bae #cool. Overall, I’d like to strike a balance between randomization/generated and a well composed book.
  • Do people feel like I just did this by hand in InDesign, no scripting? or is it obviously programmed? or both?

Code 
Having some trouble embedding syntax-colored code (WP-Syntax plugin). So here’s p5.

upload

 

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";

//many thanks to Golan and Marius for the demos 
var jsonString = b.loadString("alphabet.json");
var jsonData;

function draw() {

    b.clear(b.doc()); // clears previous output

    var numOfLetters = 3; //numOfLetters generated on colored page
    var fonts = app.fonts; //object of all fonts
    var fontOpacity = 60;
    var fontInfoSize = 15;
    var fontSize = b.width-100;
    var margin = b.width*.1;
    b.println(fonts);
    b.println("font length"+app.fonts.length);

    //yay colors
    var fontColor1 = b.color(0,0,0); //black
    var fontColor2 = b.color(255,255,255); //white
    var backgroundColor1 = b.color(243, 173, 0); //yellow
    var backgroundColor2 = b.color(234, 158, 147); //pink
    var backgroundColor3 = b.color(50, 160, 255); //blue
    var backgroundColor4 = b.color(216, 66, 30); //red
    var backgroundColor5 = b.color(27,177,91); //green

    jsonData = b.JSON.decode(jsonString);

    //Cover Page
    for (var i = 0; i0){ 
                b.opacity(printLetter, fontOpacity); 
            }
            //textframe resize to content
            printLetter.fit(FitOptions.FRAME_TO_CONTENT);

            //left page fontInfo
            //what font is it?
            var posX = -b.width + 54;
            b.textAlign(Justification.LEFT_ALIGN);
            b.fill(fontColor1);
            b.textSize(12);
            var fontInfo = b.text(fontName, posX, posY, b.width*.5,36);
            fontInfo;

            //leading 
            posY = posY + 24;
        }
    }
    //Back Cover
     b.addPage();
    for (var i = 0; i
					
					
			
Written by Comments Off on Guodu-Book Posted in Book

arialy-book

I wanted to make some fun random milkshake ingredients with a hint of nonsensical evil. At first I was going to just have normal ingredients mixed with things like “1 cup from the blood of your enemies.” After finding milkshake recipes that actually had instructions, I decided to have actual recipes with unnecessarily emotional directions. After running the code, I found that some of the directions read more sweet (since many of the hate letters were love hate letters). The contrast of either love or hate with the cooking instructions was an entertaining idea to me.

I generated my book using Processing code. The milkshake names are randomly generated from a list of negative adjectives. The ingredients are random milkshake ingredients, and the directions are a markov chain from the Rita library combining hate letters from the internet and milkshake recipes. The book definitely would’ve benefitted from some sort of images, perhaps just images from #milkshake from instagram. There was also an extreme lack of coherence. Perhaps if I’d had more recipes and hate letters I would’ve been able to tweak the markov chain to make more grammatically-correct sentences (that still had the strange mix of recipe + hate letters). The ingredients also could’ve been more entertaining with a couple strange ingredients added. I’m having a hard time accessing the end result of my book, since nothing seems very funny after reading several iterations of the code.

img_1434 img_1436

arialybookgif

Cover PDF:

aricover

Contents PDF:

arialybook

Cover Code:

import processing.pdf.*;
PImage img;
ArrayList points = new ArrayList();
int index = 0;
float[] milkshake = {104.0, 364.0, 98.0, 367.0, 90.0, 372.0, 82.0, 377.0, 78.0, 381.0, 74.0, 386.0, 74.0, 393.0, 74.0, 398.0, 78.0, 405.0, 88.0, 410.0, 98.0, 415.0, 104.0, 416.0, 113.0, 417.0, 122.0, 418.0, 131.0, 418.0, 141.0, 418.0, 148.0, 418.0, 156.0, 417.0, 163.0, 416.0, 174.0, 414.0, 185.0, 408.0, 191.0, 404.0, 197.0, 399.0, 199.0, 394.0, 198.0, 389.0, 196.0, 383.0, 182.0, 374.0, 168.0, 371.0, 165.0, 364.0, 168.0, 352.0, 202.0, 167.0, 204.0, 162.0, 203.0, 134.0, 202.0, 125.0, 76.0, 125.0, 201.0, 123.0, 198.0, 117.0, 196.0, 109.0, 188.0, 101.0, 183.0, 102.0, 177.0, 95.0, 175.0, 88.0, 176.0, 94.0, 180.0, 61.0, 180.0, 57.0, 177.0, 52.0, 147.0, 12.0, 144.0, 10.0, 139.0, 12.0, 139.0, 18.0, 171.0, 60.0, 168.0, 61.0, 168.0, 65.0, 168.0, 81.0, 165.0, 82.0, 160.0, 81.0, 160.0, 85.0, 156.0, 83.0, 144.0, 82.0, 136.0, 81.0, 136.0, 82.0, 140.0, 81.0, 139.0, 71.0, 136.0, 64.0, 130.0, 62.0, 123.0, 63.0, 127.0, 62.0, 129.0, 54.0, 126.0, 56.0, 123.0, 60.0, 123.0, 55.0, 118.0, 50.0, 117.0, 57.0, 107.0, 43.0, 107.0, 47.0, 116.0, 55.0, 113.0, 57.0, 108.0, 53.0, 106.0, 53.0, 108.0, 61.0, 101.0, 55.0, 102.0, 59.0, 98.0, 65.0, 107.0, 66.0, 104.0, 66.0, 102.0, 72.0, 100.0, 79.0, 105.0, 87.0, 96.0, 91.0, 86.0, 102.0, 85.0, 105.0, 84.0, 111.0, 81.0, 112.0, 76.0, 112.0, 75.0, 118.0, 74.0, 124.0, 69.0, 125.0, 70.0, 163.0, 103.0, 352.0, 108.0, 355.0, 105.0, 364.0};

PShape s;
PFont font;

void setup() {
  beginRecord(PDF, "everything.pdf");
  size(1000, 500);
  
  font = createFont("RalewayThin.ttf", 30);
  textFont(font);
  background(0);
  
  s = createShape();
  s.beginShape();
  for (int i = 2; i < milkshake.length; i+=2) {
    s.vertex(milkshake[i-2] + 510, (milkshake[i-1]+20));}
  

  s.endShape(CLOSE);
  shape(s, 0, 0);
  text("Emotional Shakes", 720, 300);
   endRecord();
  //exit();
}


void draw() {
  
}
void mousePressed() {
 
}

Contents Code:

import processing.pdf.*;
import rita.*;
RiLexicon lex = new RiLexicon();
PFont font;
int count = 0;

RiMarkov markov;
String line = " ";
String[] files = { "../data/cAH.txt", "../data/recipes.txt", "../data/hateLetters.txt", 
  "../data/moreMilkshakes.txt", "../data/bakingBook.txt"};
String[] ingList = {"1 large or 2 small bananas", 
  "vanilla ice cream", 
  "milk", 
  "sugar", 
  "2 1/2 cups frozen blueberries", 
  "1 1/4 cups apple juice", 
  "1 cup vanilla frozen yogurt", 
  "1/4 cup skim milk", 
  "3/4 teaspoon ground cinnamon", 
  "4 scoops vanilla ice cream or frozen yogurt", 
  "3/4 cup cold milk", 
  "1/4 cup chocolate syrup", 
  "3 maraschino cherries, stems removed", 
  "whipped topping & additional cherry", 
  "1/4 cup milk", 
  "2 scoops vanilla ice cream", 
  "1 tablespoon chocolate syrup", 
  "2 tablespoons peanut butter", 
  "3 cups vanilla ice cream", 
  "1 cup chocolate milk", 
  "3 heaping tablespoons chocolate powder", 
  "2 medium sized chocolates", 
  "3 scoops vanilla ice cream", 
  "2 tablespoons chocolate chips", 
  "1/2 banana", 
  "1 tablespoon instant coffee", 
  "2 tablespoon chocolate syrup", 
  "1 cup milk", 
  "1 teaspoon vanilla", 
  "1 cup milk", 
  "1 pkg. (4-serving size) Jello, any flavor", 
  "1 pint vanilla ice cream, softened", 
  "2 cups (480ml) vanilla ice cream", 
  "1 cup (240ml) whole milk", 
  "1/4 cup (60ml) half & half", 
  "2 1/2 tablespoons (35g) sugar", 
  "1/8 teaspoon vanilla extract", 
  "4 scoops vanilla ice cream", 
  "1 cup peaches, peeled, pitted, cut up into chunks", 
  "1 cup cold orange juice", 
  "2 strawberries (garnish)", 
  "1/4 cup milk", 
  "1 scoop lime sherbet", 
  "1 scoop vanilla ice cream", 
  "handful of ice cubes", 
  "1 cup milk", 
  "2 teaspoons vanilla syrup", 
  "1 teaspoon malted milk powder", 
  "2 scoops vanilla ice cream", 
  "1 cup chocolate or vanilla ice cream", 
  "1/2 cup whole milk", 
  "3 - 4 tablespoons chocolate syrup", 
  "1 cup milk", 
  "2 scoops of your favorite ice cream", 
  "2 tablespoons instantmalted milk powder", 
  "8 ounces chai concentrate", 
  "14 ounces vanilla ice cream", 
  "vanilla ice cream", 
  "milk", 
  "about 6-8 Oreo cookies", 
  "4 oz. canned pumpkin, chilled", 
  "1-1/2 cups cold low fat milk", 
  "8 teaspoons sugar", 
  "1/8 teaspoon cinnamon", 
  "1 teaspoon vanilla", 
  "1/3 cup frozen grape juice concentrate, thawed", 
  "1 banana, peeled and sliced", 
  "1 cup milk", 
  "1/2 cup ice cubes", 
  "1 cup milk", 
  "2 scoops vanilla ice cream", 
  "4 large fresh strawberries (or 1/2 cup frozen strawberries)", 
  "1 large banana, chopped", 
  "5 cups chocolate or vanilla ice cream", 
  "2 1/2 cup milk", 
  "1 1/4 cup canned unsweetened coconut milk", 
  "1 large pineapple, peeled, cored, chopped", 
  "5 ripe bananas, peeled", 
  "3 medium papayas, peeled, seeded, chopped", 
  "32 strawberries, hulled", 
  "1/2 cup milk", 
  "1/2 cup whipped cream", 
  "2 tablespoons cherry juice", 
  "1/2 cup ice", 
  "2 - 3 scoops vanilla ice cream or frozen yogurt", 
  "1/3 cup cold milk", 
  "3 tablespoons chocolate malt syrup", 
  "whipped cream topping"};


int x = 160, y = 240;
int ingIndex0 = int(random(0, 87));
int ingIndex1 = int(random(0, 87));
int ingIndex2 = int(random(0, 87));
int ingIndex3 = int(random(0, 87));
String hateAdj = "";
String[] hateWords;
int offset = 40;


void setup()
{
  
  font = createFont("CrimsonText-Roman.ttf", 16);
  textFont(font);
  hateWords = loadStrings("hateWords.txt");
  hateAdj = hateWords[int((random(0, 526)))];
  size(500, 500, PDF, "arialyBook.pdf");

  fill(0);
  //textFont(createFont("times", 16));

  // create a markov model w' n=3 from the files
  markov = new RiMarkov(2);
  markov.loadFrom(files, this);
}

void draw()
{ 
  PGraphicsPDF pdf = (PGraphicsPDF) g;  // Get the renderer
  
  background(250);
  text(hateAdj + "Shake", 50, 30 + offset);
  text("Ingredients", 50, 80 + offset);
  text(ingList[ingIndex0], 60, 105 + offset);
  text(ingList[ingIndex1], 60, 130 + offset);
  text(ingList[ingIndex2], 60, 155 + offset);
  text(ingList[ingIndex3], 60, 180 + offset);
  text("Directions", 50, 215 + offset);
  text(line, x+10, y+175 + offset, 400, 400);
  
  hateAdj = hateWords[int((random(0, 526)))];
  ingIndex0 = int(random(0, 87));
  ingIndex1 = int(random(0, 87));
  ingIndex2 = int(random(0, 87));
  ingIndex3 = int(random(0, 87));
  if (!markov.ready()) return;

  x = y = 50;
  String[] lines = markov.generateSentences(3);
  line = RiTa.join(lines, " ");
  if (count == 0) {
    background(255);
    textAlign(CENTER);
    text("the intersections of milkshakes and hate letters", 250, 250);
    textAlign(LEFT);
}
  if (frameCount == 42) {
    exit();
  } else {
    pdf.nextPage();  // Tell it to go to the next page 
  }
  count += 1;
}

void mouseClicked(){
  
  
}

Written by Comments Off on arialy-book Posted in Book

Antar-book

OKAY BABE

Summary:

  • A French fold book with typography wallpapers, with 70’s porn between the pages
  • The content is from a random selection of the 100 most recent tweets that contained the phrase “okay babe”
  • The tweets were then cleaned up to only contain the text that was proper English. I also took out the URLs, @mentions, acronyms, sounds and also the phrase “okay babe” itself.
  • Each page contains one of the cleaned tweets, rendered with a basil.js script
  • There were three scripts:
    • Growth: each character of the tweet grows in point size linearly
    • Sine: each character of the tweet grows in point size sinusoidally
    • DoubleSine: the same as sine, but additionally the baseline shift of each character grows sinusoidally
  • Scanned pages from Playboy’s March 1970 issue are the inside page images

Here’s a video of Professor Levin flipping through my OKAY BABE book:

I have been taking this class as an opportunity to explore my own style and creative voice, and this project was a particularly fun way to do some discovering. At the beginning of sophomore year, I conceived the phrase OKAY BABE, and it has since made appearances in my work, and has also become my signature on my Instagram. I use OKAY BABE as a filler text, it’s my lorum ipsum, that can carry very different typographic voices depending on the visual context that it is in.

I wanted to use this project to see how other people use “okay babe”, and what voices they are using.  I also wanted to have a very type heavy book and explore the power of basil.js. I first thought of going to NPR or NYT to search for articles that contained “okay babe”. This was, probably obviously, pretty unsuccessful. The phrase is so casual and colloquial that it wasn’t really used in more formal conversations that happen during NPR stations or articles in the Times. This is when I decided to turn to twitter, where I had a new, opposite, challenge. The language of twitter can be so causal that some tweets only contained phrases like, “@Tayatyy hahahahah yaa wtvr okay babe!!!”. I still wanted my content to be sophisticated language, but said in a casual context.

As a communication designer I really felt in my realm here. I love book design and typography, and just learned how to bind books by hand. In terms of a visual context I wanted to use this program to make adjustments to type that wouldn’t be really possible by hand. A lot of my work plays with repetition and pattern, so I saw this as an opportunity to mix this with my love for type. However, pages of repetitive patterns are great, but they are repetitive. I wanted to incorporate images without obstructing the type patterns. After learning about book binding this semester in my communications studio, I thought that this project would be a great opportunity to use french fold perfect binding. This is where the outside edge of the pages are not cut, allowing the reader to peak in between two pages. This is where I thought that I could create a nice narrative for the book: While the pages look like repetitive (perhaps dull) wallpapers, between the pages (behind the walls), reveals risqué images. This allowed for the perfect opportunity to borrow my friend’s vintage 1970’s March issue of Playboy magazine. In the 70’s the pornography industry was significantly more conservative than it is now. The girls are almost never completely nude, and most of the time they are fairly covered.

screen-shot-2016-10-31-at-12-47-14-pmscreen-shot-2016-10-31-at-12-47-43-pmscreen-shot-2016-10-31-at-12-49-36-pmscreen-shot-2016-10-31-at-12-50-04-pmscreen-shot-2016-10-31-at-12-50-44-pmscreen-shot-2016-10-31-at-12-50-54-pmscreen-shot-2016-10-31-at-12-51-02-pmscreen-shot-2016-10-31-at-12-45-00-pmscreen-shot-2016-10-31-at-12-45-14-pm screen-shot-2016-10-31-at-12-45-37-pm

screen-shot-2016-10-31-at-12-41-59-pm screen-shot-2016-10-31-at-12-42-18-pm screen-shot-2016-10-31-at-12-42-26-pm screen-shot-2016-10-31-at-12-42-37-pm screen-shot-2016-10-31-at-12-43-37-pm

Written by Comments Off on Antar-book Posted in Book

Kelc-Book

bookcover

kelc

Book Versions:

Words to Live By takes fairly well-known quotes from poets and writers and mashes them with lines from current-day rappers. Below are some excerpts:

exp8 exp7

exp1

Process & Code

Python Program – pulls lyrics from given artists from LyricsNMatch.com and saves .txt file using Unirest.

Processing Code – reads from .txt files produced above and sews together quotes + names.

Conceptually my aim was to work on blending two different vernaculars into one text to create an odd and unexpected twist on phrases, sayings, and lines many people recognize. To start I sifted through the various song lyrics API’s available to me. I ended up using the “LyricsNMusic” API from marketplace.mashape.com. It allowed search inputs to range from artist and song name to keywords and lyric excerpts. The biggest issue I ran into with this project was simply working with java; I knew most of the manipulation I would be doing would be in rita.js but attempting to download every installer, package and library and required to even begin using this API (and then over-Google every error message I received) became very taxing on both me and my laptop. So I opted for Python.

I first ran my Python program, which produced lyric .txt files of whatever artist name is put in, on an array of about 37 rappers I could come up with. Then in Processing I iterated through those documents as well as another “50 Famous Quotes from Poets” .txt file and sewed different lines together through a common word like “in”, “and”, or “woman”. The output wasn’t as seamless and consistent as I originally intended but it worked surprisingly similar to a more efficient maneuver I would’ve used in rita.js. All in all, sometimes the less than desirable method still gets you a result you are aiming for.


Here’s a video of the professor, flipping through my book:

Written by Comments Off on Kelc-Book Posted in Book

Krawleb-Book

by any other nameexperiments in synonymous corruption

PDF (600 kb): ./wp-content/uploads/2016/10/krawleb-book.pdf

baon1

baon2

baon3

My book is a collection of 14 pieces of writing that have been transformed and corrupted through a synonym-swapping algorithm. The source text comes from monologues, song lyrics, and poems, each printed in its original form, then recursively synonym-swapped 5 times. The resulting text is a twisted, often out of context, but somewhat related derivative of the original concepts. Inspired by the famous Shakespeare passage “a rose by any other name would smell as sweet”, I explored what happened when we use ‘other names’ for things to the point of destruction.

The software pipeline for this project begins with a Python for Processing program that parses .txt files of both the original source content and a large .txt thesaurus. The program looks at each word, identifies words with synonyms in the thesaurus, and then replaces them. This data is then written to CSV, tagging each word that has been swapped each iteration, as well as tagging ends of lines to preserve the line breaks.

These CSVs are then fed into a Basil.js program that iterates over an array of CSV files, printing the text of each iteration on a new page, and italicizing each swapped word in each recursive iteration.

Ultimately, this project was an interesting experiment, but was a bit unpolished and buggy as I began to run out of time as the deadline approached. Some words are swapped into non-english synonyms, and some synonyms appear nonsensical or unrelated. However, in there chaos there are moments of beauty. Sometimes the text is morphed into new and humorous or beautiful new ideas through the process.

Additionally, the visual form is very minimal, which I would have liked to develop more, but the Basil.js workflow is rather unfriendly to rapid iteration, so I kept things as clean as possible for the final. That being said, I do like the austerity of the small undecorated text on otherwise blank pages.

Github here

PDFs of cover and spreads


Code below:

import random
from random import randrange
import re
import csv
import sys

textName = 'loveCallsUs'

source = open('texts/'+textName+'.txt', 'r')
ts = open('texts/synAntNoHyph.txt', 'r')
ts2 = open('texts/MobyTs.txt', 'r')

export = open('export.txt', 'w')
csvExport = open('exports/'+textName+'.csv', 'wb')

def setup():
    size(100, 100)

    rawTs = ts.readlines()
    rawTs2 = ts2.readlines()
    
    rawlines = source.readlines()
    lines = []
    iterations = []
    numIterations = 5 

    for raw in range(len(rawlines)):
        aLine = rawlines[raw].split()
        lines.append(aLine)
        
    #initialize csv with source
    writer = csv.writer(csvExport)
    writer.writerow( ('word', 'isNew', 'iteration', 'endLine') )
    print lines
    for x in range(len(lines)):
        isEnd = 0 #is it the end of a line?
        for y in range(len(lines[x])):
            word = lines[x][y]
            if y == len(lines[x])-1: #adjusted for index
                isEnd = 1
            writer.writerow( (word, '0' , '0' , isEnd ) )
        
    #first, print original text
    for line in lines:
        joined = ' '.join(line)
        print>>export, joined
    print>>export, '\n'

    for z in range(numIterations):
        # Find Synonyms
        for i in range(len(lines)):  # loop over lines
            for j in range(len(lines[i])):  # loop through words
                
                currentWord = re.sub(r'[^\w\s]','',lines[i][j]).title() #remove punctuation
                # print currentWord
                
                found = False #reset found (in first thesaurus)
                
                for k in range(len(rawTs)):  # loop through ts (first thesaurus)
                    if random.random() > 0.0:
                        if rawTs[k].startswith(currentWord+'.'): #if it's a period, grab the word right after
                                # print rawTs[k]
                                index = random.randint(1,len(rawTs[k].split())-1)
                                synonym = rawTs[k].split()[index]
                                synonym = str('_'+synonym)
                                found = True
                        if rawTs[k].startswith(currentWord+','): #if it's a comma, grab the second word
                                # print rawTs[k]
                                if len(rawTs[k].split())-1 >= 2:
                                    index = random.randint(2,len(rawTs[k].split())-1)
                                else:
                                    index = 1
                                synonym = rawTs[k].split()[index] 
                                synonym = str('_'+synonym)
                                found = True
    
                if found == True:
                    if j > 0:
                        synonym = synonym.lower()
                        synonym = re.sub(r'[^\w\s]','',synonym)
                        synonym = synonym.strip(';')
                    lines[i][j] = synonym
         
        #write to CSV 
        print lines                                     
        for x in range(len(lines)):
            for y in range(len(lines[x])):
                isSyn = 0 #is it a synonym?
                isEnd = 0 #is it the end of a line?
                if lines[x][y].startswith('_'): #detect if its a synonym
                    lines[x][y] = lines[x][y][1:] #strip synonym identifier
                    isSyn = 1
                if y == len(lines[x])-1: #adjusted for index
                    isEnd = 1
                word = lines[x][y]
                writer.writerow( (word, isSyn , (z+1) , isEnd) )
                # print word
                    
            # print str(lines[x])
            joined = ' '.join(lines[x])  
            print>>export, joined
        print>>export, '\n'
            
        iterations.append(lines)
    noStroke()
    fill(0,255,0)
    ellipse(width/2,height/2,80,80)
    
    csvExport.close()

Basil/InDesign code:

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "../../../bundle/basil.js";

var csvArray = [
'rose.csv',
'hammy.csv',
'desire.csv',
'leisure.csv',
'enigmas.csv',
'israfil.csv',
'tooLate.csv',
'aRitual.csv',
'folkMeta.csv',
'sasquach.csv',
'souvenir.csv',
'gazzillion.csv',
'loveCallsUs.csv',
'spottieOttie.csv' ];

var csvData;

var genCover = false;
var reset = true;

//--------------------------------------------------------
function setup() {

  // Clear the document at the very start. 
  if (reset == true){
    b.clear (b.doc());
  }

  if (genCover == true){

  // Make a title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Helvetica","Light"); 
  b.textAlign(Justification.LEFT_ALIGN); 
  b.text("By Any Other Name", 72,72,360,36);
  b.text("Kaleb Crawford, Fall 2016", 72,108,360,36);

  } //end Gen Cover

  //72 points in an inch
  var margin = 72

  var titleX = 72; 
  var titleY = 72;
  var titleW = 72;
  var titleH = 72;

  var passageX = margin + 18;
  var passageY = margin * 2;
  var passageW = margin * 3;
  var passageH = b.height-margin*2;

  var innerText;
  var mainFrame;
  var tempFrame;

  //////////////////////CSV Array Loop//////////////////////
  for (var texts = 0; texts < csvArray.length; texts++){

  var csvString = b.loadString(csvArray[texts]);
  csvData = b.CSV.decode( csvString );
  b.println("Number of elements in CSV: "+csvData.length);

  var totalIterations = parseInt(csvData[csvData.length-1].iteration);
  var currentIteration = 0;

  ////////////////////// Iterations Loop////////////////////
  for (var i = 0; i <= totalIterations; i++) {

    b.addPage(); // Create the next page.

    //Create the frame
    b.fill(0);
    b.textSize(8);
    b.textFont("Garamond","Regular"); 
    b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN );
    mainFrame = b.text("", passageX, passageY, passageW, passageH);

    currentIteration = i;
    innerText = ''; //initialize inner text

    ////////////////////Word Loop///////////////////
    for (var l = 0; l < csvData.length ; l ++ ){
      if (csvData[l].iteration == currentIteration){

        innerText = csvData[l].word+" ";

        if (csvData[l].isNew == 1){
          b.textFont("Garamond","Italic");
        }
        else{ b.textFont("Garamond","Regular"); }

        if (csvData[l].endLine == 1){
          innerText += "\n";
        }

        tempFrame = b.text(innerText, 0, 0, 100, 100);

        // mainFrame.contents += tempFrame.contents;

        tempFrame.paragraphs[0].move(LocationOptions.after,mainFrame.insertionPoints[-1]);

        tempFrame.remove();

      } //end if currentIteration check
    } // end line loop
    b.println('text '+(texts+1)+" iteration "+currentIteration);
  } //end iteration loop
}//end csvArray loop
} // end setup

// This makes it all happen:
b.go(); 
Written by Comments Off on Krawleb-Book Posted in Book

hizlik-book

Year One is a book of cityscapes. Each city represents the daily volume of text messages that my girlfriend and I sent each other during our first year of dating. There are 366 total cities (I included both our first day, and first anniversary), and each building represents an hour in the day (24 total buildings, at most), in the manner of a bar chart.

cover_front

Each city is rendered with a watercolor/painted effect. The book was mainly generated using Processing, but other programming languages were used as well, which served various purposes in the creation of the imagery within the pages.

365

As described in the process video (below), I wanted to create a project based on the texts that my girlfriend sent to each other during our first year of dating, which was long-distance for most of the time. The other thing I wanted to do was create generative cityscapes (I have a fascination with cities). After being unable to pick between the two concepts, I put them together. Essentially, the total significance of all the texts per hour (so not the amount of texts, but the total content length) dictates the height of a building, while the number of images sent are represented by the various attachments to the buildings (antennas, water towers, balconies, “blocks/vents” on the roof, etc. I used multiple languages to create this project, detailed below. One of the significant issues for this was creating the painterly effect, which in the end I think turned out exactly as I had hoped it would. I took inspiration from artist Michael Tompsett for the artistic style of my generated imagery.

screen-shot-2016-10-28-at-1-48-53-am screen-shot-2016-10-28-at-1-49-04-am

The most time-consuming process was the creation of the variety of buildings and building parameters. Each building consists of differently modified versions of vertex-based shapes. I believe I succeeded in everything I intended to do, which surprises me because normally I either run out of time or am unable to achieve my vision. This was one of the more multi-step and complicated projects I’ve undertaken, so I was surprised I was able to not only achieve the exact image I had in my head, but also within record time (I started Wednesday night, finished by Saturday afternoon). I love how the buildings look, am pleased with their variety (although it could always use more) and love the way the watercolor effect looks. If I had to change anything, I would change the % possibility for some types of more unique buildings (to a lower chance), and also add more brush types and more “floors” the buildings rest on, for more variety. (You can see some repetition if you pay attention).

The numbers at the bottom of each page represent the total number of text messages, and total volume of characters that we exchanged. Golan expressed surprise that we sent each other (in some cases) hundreds of text messages per day. It’s true.

Here’s a video of Golan flipping through the book:

FILES

Short excerpt of the book (8MB PDF): hizlik_book_short.pdf

You can download this PDF and all the source code from my GitHub repo.

CODE

cover_back

 


Shell/Bash Script: Export text messages from Apple Messages SQLite database

if [ $# -lt 1 ]; then
    echo "Enter a iMessage account (email of phone number i.e +33616.....) "
fi
login=$1
    
sqlite3 ~/Library/Messages/chat.db "
select '[str][' as 'ph1',datetime(date + strftime('%s', '2001-01-01 00:00:00'), 'unixepoch', 'localtime') as date,']' as 'ph2','[' as 'ph2',is_from_me,']' as 'ph2',NULL as 'filename',text from message where handle_id=(
select handle_id from chat_handle_join where chat_id=(
select ROWID from chat where guid='iMessage;-;$1')
)
union all
select '[att][' as 'ph1',datetime(date + strftime('%s', '2001-01-01 00:00:00'), 'unixepoch', 'localtime') as date,']' as 'ph2','[' as 'ph3',is_from_me,']' as 'ph2',NULL as 'text',filename from attachment,message_attachment_join,message where attachment.rowid = message_attachment_join.attachment_id and message_attachment_join.message_id = message.rowid and message.cache_has_attachments=1 and message.handle_id=(
select handle_id from chat_handle_join where chat_id=(
select ROWID from chat where guid='iMessage;-;$1')
) order by date" | sed 's/\|1\|/H/g;s/\|0\|/N/g;s/\|//g' > iMessages$1.txt

 

Python: Analyze texts for number, total characters, and amount of attachments sent (per hour)

# -*- coding: utf-8 -*-
import glob
import os
import re
import string

from datetime import datetime,timedelta

filenames = []
texts = []
data = {}

metadata = {
	'total_N_str': 0,
	'total_N_att': 0,
	'total_N_str_len': 0,
	'max_N_len': 0,
	'total_H_str': 0,
	'total_H_att': 0,
	'total_H_str_len': 0,
	'max_H_len': 0
}

def getLogs():
	cwd = os.getcwd()
	os.system("cd '" + cwd + """'
./imbfull.sh +19084212107
./imbfull.sh nicole.hladick@gmail.com""")

def getFileNames():
	for filename in glob.glob('*.txt'):
		if "iMessage" in filename: filenames.append(filename)

def displayFileList():
	print len(filenames),"files found:"
	print '\n'.join(filenames)

def openFile(s):
	print "Openning file...",
	with open(s, 'r') as f: 
		readList = f.readlines()
	print "			File successfully open."
	return readList

def parseText(text):
	# search for [type][YYYY-MM-DD HH:MM:SS][FROM]
	pattern = re.compile('^\[[a-z]{3}\]\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]\[[A-Z]\]')

	meta = re.search(pattern, text).group()
	t_type = meta[1:4]
	t_from = meta[-2:-1]
	t_date = datetime.strptime(meta[6:25], '%Y-%m-%d %H:%M:%S')
	t_str = text.replace(meta,"").rstrip()

	return t_type, t_from, t_date, t_str

def listTexts(file, filename):
	print "Listing messages...",

	# search for [type][YYYY-MM-DD HH:MM:SS][FROM]
	pattern = re.compile('^\[[a-z]{3}\]\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]\[[A-Z]\]')

	for line in file:
		line = line.rstrip()
		if re.match(pattern, line):
			texts.append(line)
		else:
			texts[-1] += line

	print "		Messages successfully listed."

def runMessageExtractor(s):
	print "\nExtracting text IDs on %s:"%(s)
	listTexts(openFile(s), s)
	# H_att,N_att,H_txt,N_txt = countTexts(readList,s)
	# texts.append((H_att,N_att,H_txt,N_txt))

def analyze():
	global total_N_str, total_N_att, total_H_str, total_H_att

	print "Analyzing messages...",
	#initialize time range
	start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
	while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): 
		data[start_date.strftime('%Y-%m-%d %H')] = { 
			'H': { 'txt_count':0, 'len':0, 'att_count':0 },
			'N': { 'txt_count':0, 'len':0, 'att_count':0 }
		}
		start_date += timedelta(hours=1)
	#populate data
	for text in texts:
		t_type, t_from, t_date, t_str = parseText(text)
		if t_type == "str":
			if t_from == "H":
				metadata['total_H_str'] += 1
				metadata['total_H_str_len'] += len(t_str)
				metadata['max_H_len'] = max(metadata['max_H_len'],len(t_str))
			else:
				metadata['total_N_str'] += 1
				metadata['total_N_str_len'] += len(t_str)
				metadata['max_N_len'] = max(metadata['max_N_len'],len(t_str))
		else:
			if t_from == "H":
				metadata['total_H_att'] += 1
			else:
				metadata['total_N_att'] += 1
		if t_date.strftime('%Y-%m-%d %H') in data:
			data[t_date.strftime('%Y-%m-%d %H')][t_from]['txt_count'] += 1
			if t_type == "att":
				data[t_date.strftime('%Y-%m-%d %H')][t_from]['att_count'] += 1
			else:
				data[t_date.strftime('%Y-%m-%d %H')][t_from]['len'] += len(t_str)
	print "		Analysis complete."
def writeData():
	print "Writing data...",
	with open("h_data.txt", 'w') as f:
		start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
		prev_day = -1
		while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): if prev_day != start_date.day: if prev_day >= 0:
					f.write("\n")
				f.write(start_date.strftime("%B %d").lstrip("0").replace(" 0", " "))
				prev_day = start_date.day
			txt_count = data[start_date.strftime('%Y-%m-%d %H')]['H']['txt_count']
			t_len = data[start_date.strftime('%Y-%m-%d %H')]['H']['len']
			att_count = data[start_date.strftime('%Y-%m-%d %H')]['H']['att_count']
			f.write("-%d&%d&%d"%(txt_count, t_len, att_count))
			start_date += timedelta(hours=1)
		f.close()
	with open("n_data.txt", 'w') as f:
		start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
		prev_day = -1
		while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): if prev_day != start_date.day: if prev_day >= 0:
					f.write("\n")
				f.write(start_date.strftime("%B %d").lstrip("0").replace(" 0", " "))
				prev_day = start_date.day
			txt_count = data[start_date.strftime('%Y-%m-%d %H')]['N']['txt_count']
			t_len = data[start_date.strftime('%Y-%m-%d %H')]['N']['len']
			att_count = data[start_date.strftime('%Y-%m-%d %H')]['N']['att_count']
			f.write("-%d&%d&%d"%(txt_count, t_len, att_count))
			start_date += timedelta(hours=1)
		f.close()
	print "			Write complete."

def writeDataTogether():
	print "Writing data...",
	with open("data.txt", 'w') as f:
		start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
		prev_day = -1
		while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): if prev_day != start_date.day: if prev_day >= 0:
					f.write("\n")
				f.write(start_date.strftime("%B %d, %Y").lstrip("0").replace(" 0", " "))
				prev_day = start_date.day
			txt_count = data[start_date.strftime('%Y-%m-%d %H')]['H']['txt_count'] + data[start_date.strftime('%Y-%m-%d %H')]['N']['txt_count']
			t_len = data[start_date.strftime('%Y-%m-%d %H')]['H']['len'] + data[start_date.strftime('%Y-%m-%d %H')]['N']['len']
			att_count = data[start_date.strftime('%Y-%m-%d %H')]['H']['att_count'] + data[start_date.strftime('%Y-%m-%d %H')]['N']['att_count']
			f.write("-%d&%d&%d"%(txt_count, t_len, att_count))
			start_date += timedelta(hours=1)
		f.close()
	print "			Write complete."

def runTextCounter():
	getLogs()
	getFileNames()
	displayFileList()
	print "\nBeginning chatlog indexing..."
	for chatlog in filenames:
		runMessageExtractor(chatlog)
	print ""

	analyze()
	writeDataTogether()

	# only within time range
	non0len_H = 0
	non0count_H = 0
	maxlen_H = 0
	non0len_N = 0
	non0count_N = 0
	maxlen_N = 0

	start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
	while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): t_len = data[start_date.strftime('%Y-%m-%d %H')]['H']['len'] if t_len > 0:
			non0len_H += t_len
			non0count_H += 1
			maxlen_H = max(maxlen_H, t_len)
		t_len = data[start_date.strftime('%Y-%m-%d %H')]['N']['len']
		if t_len > 0:
			non0len_N += t_len
			non0count_N += 1
			maxlen_N = max(maxlen_N, t_len)
		start_date += timedelta(hours=1)

	print "\nChatlog indexing is complete.\n"
	print "%d total messages: %d by Hizal and %d by Nicole."%(metadata['total_H_str']+metadata['total_N_str'], metadata['total_H_str'], metadata['total_N_str'])
	print "%d total attachments: %d by Hizal and %d by Nicole."%(metadata['total_H_att']+metadata['total_N_att'], metadata['total_H_att'], metadata['total_N_att'])
	print "%d ave characters: %d for Hizal (max %d) and %d for Nicole (max %d)"%((metadata['total_H_str_len']+metadata['total_N_str_len'])*1.0/(metadata['total_H_str']+metadata['total_N_str']), metadata['total_H_str_len']*1.0/metadata['total_H_str'], metadata['max_H_len'], metadata['total_N_str_len']*1.0/metadata['total_N_str'], metadata['max_N_len'])
	print "%d ave characters per hour: %d for Hizal (max %d) and %d for Nicole (max %d)"%((non0len_H + non0len_N)*1.0/(non0count_H + non0count_N), non0len_H*1.0/non0count_H, maxlen_H, non0len_N*1.0/non0count_N, maxlen_N)
	

runTextCounter()

 


Processing: Generating cityscape masks using brush strokes and random buildings based on text analysis

import java.util.*; 
boolean debug = false;

ArrayList<String[][]> texts = new ArrayList<String[][]>();
int index = 0;
int hour = 0;
boolean var3used = false;
boolean var4used = false;
boolean thinused = false;

// VARIATIONS
float overlap = 10;
float ignore = 10;
float xscap = 25;
float smallcap = 50;
float medcap = 200;
float tallcap = 500;

// CITY VARIABLES
float citystartx;
float citystarty;
float building_width;
ArrayList trees = new ArrayList();

// PAINT
ArrayList floors = new ArrayList();
ArrayList lefts = new ArrayList();
ArrayList rights = new ArrayList();
ArrayList mids = new ArrayList();

void setup() {
  size(1700,1700); //start 350 in from 1700
  String[] fileLines = loadStrings("../txtcounter/data.txt");
  for(int i=0; i<fileLines.length; i++)
    texts.add(parseData(fileLines[i]));
  
  // trees
  for(int i=0; i<12; i++)
    trees.add(loadImage("greenery/s_treeTop"+i+".png"));
  
  // floors
  for(int i=0; i<10; i++)
    floors.add(loadImage("brushes/floors/floor"+i+".png"));
  
  // lefts
  for(int i=0; i<12; i++)
    lefts.add(loadImage("brushes/lefts/left"+i+".png"));
  
  // rights
  for(int i=0; i<12; i++)
    rights.add(loadImage("brushes/rights/right"+i+".png"));
  
  // mids
  for(int i=0; i<46; i++) mids.add(loadImage("brushes/mids/mid"+i+".png")); citystartx = 350; citystarty = (height-350)-(height-700)/3.0; building_width = (width-700)/24.0; background(255); //buildCity(texts.get(index)); println(getMetadata(texts.get(index))); } void draw() { // draw background if(hour == 0) { int floor = int(random(floors.size())); image(floors.get(floor),0,-2); int left = int(random(lefts.size())); if(round(random(2))>0)
      image(lefts.get(left),0,0);
    int right = int(random(rights.size()));
    if(round(random(2))>0)
      image(rights.get(right),0,0);
    for(int i=0; i<round(random(4,6)); i++) { int mid = int(random(mids.size())); image(mids.get(mid),random(-100,100),0); } hour ++; } // draw template String[][] data = texts.get(index); if(hour>0 && hour<data.length) {
    buildCity(data, hour);
    hour++;
  }
}

void reset() {
  hour = 0;
  background(255);
  var3used = false;
  var4used = false;
  thinused = false;
}

void keyPressed() {
  if (key == 'b' || key == 'B') {
    index--;
    if(index<0) index = texts.size()-1; println(getMetadata(texts.get(index))); reset(); } if (key == 'n' || key == 'N') { index++; if(index >= texts.size()) index = 0;
    println(getMetadata(texts.get(index)));
    reset();
  }
  if (key == 's' || key == 'S') {
    String filename = "drawings/"+index+" "+texts.get(index)[0][0]+".png";
    saveFrame(filename);
  }
  if (key == 'r' || key == 'R') {
    reset();
  }
}

String getMetadata(String[][] data){
  String info = data[0][0]+" ";
  int texts = 0;
  int att = 0;
  for(int i=1; i<data.length; i++) {
    texts += parseInt(data[i][0]);
    att += parseInt(data[i][2]);
  }
  info += "("+texts+" texts, "+att+" attachments)";
  return info;
}

void buildCity(String[][] data, int i) {
  overlap = random(10);
  float limitter = 8000; //ave 755
  float hval = map(parseInt(data[i][1]), 0, limitter, 0, height/2);
  if(hval < smallcap) { //is small fill(255,0,0,128); } else if(hval >= smallcap && hval < medcap) { //is med fill(0,255,0,128); } else if(hval >= medcap && hval < tallcap) { //is tall
    fill(0,0,255,128);
  } 
  else { //is super tall
    fill(128,128,128,128);
  }
  if(debug) {
    noStroke();
    rect(citystartx+building_width*(i-1)-overlap, citystarty, building_width+overlap, random(-100,-200));
  }
  if(hval != 0) {
    if(hval <= ignore && parseInt(data[i][2])>0)
      attGroup0(citystartx+building_width*(i-1)-overlap, citystarty, building_width+overlap, parseInt(data[i][2]));
    else
      randomBuilding(citystartx+building_width*(i-1)-overlap, citystarty, building_width+overlap, hval, parseInt(data[i][2]));
  }
}

void randomBuilding(float startx, float starty, float w, float h, int numAtt) {
  // is too small
  if(h <= ignore) {
    var11(startx, starty, w, h, numAtt);
  }
  
  // is xs
  else if(h < xscap) {
    if(round(random(1)) == 0)
      var11(startx, starty, w, h, numAtt);
    else
      var13(startx, starty, w, h, numAtt);
  }
  
  // is small
  else if(h < smallcap) {
    int choice = int(random(100));
    if(choice < 30)
      var9(startx, starty, w, h, numAtt);
    else if(choice < 60)
      oldRoof(startx, starty, w, h, numAtt);
    else if(choice < 90)
      slantRoof(startx, starty, w, h, numAtt);
    else
      basic(startx, starty, w, h, numAtt);
  }
  
  // is medium
  else if(h < medcap) { 
    int choice = int(random(8));
    if(choice == 0)
      oldRoof(startx, starty, w, h, numAtt);
    else if(choice == 1) {
      if(thinused)
        randomBuilding(startx, starty, w, h, numAtt);
      else
        thin(startx, starty, w, h, numAtt);
    }
    else if(choice == 2)
      slantRoof(startx, starty, w, h, numAtt);
    else if(choice == 3)
      basic(startx, starty, w, h, numAtt);
    else if(choice == 4)
      stairstep(startx, starty, w, h, numAtt);
    else if(choice == 5)
      angled(startx, starty, w, h, numAtt);
    else if(choice == 6)
      bevel(startx, starty, w, h, numAtt);
    else
      roundRoof(startx, starty, w, h, numAtt);
  }
  
  // is tall
  else if(h < tallcap) { 
    int choice = int(random(14));
    if(choice == 0)
      var1(startx, starty, w, h, numAtt);
    else if(choice == 1) {
      if(var3used)
        randomBuilding(startx, starty, w, h, numAtt);
      else
        var3(startx, starty, w, h, numAtt);
    }
    else if(choice == 2) {
      if(var4used)
        randomBuilding(startx, starty, w, h, numAtt);
      else
        var4(startx, starty, w, h, numAtt);
    }
    else if(choice == 3)
      var8(startx, starty, w, h, numAtt);
    else if(choice == 4)
      slant(startx, starty, w, h, numAtt);
    else if(choice == 5)
      stairstep(startx, starty, w, h, numAtt);
    else if(choice == 6)
      bevel(startx, starty, w, h, numAtt);
    else if(choice == 7)
      slice(startx, starty, w, h, numAtt);
    else if(choice == 8)
      angled(startx, starty, w, h, numAtt);
    else if(choice == 9)
      blockRoof(startx, starty, w, h, numAtt);
    else if(choice == 10)
      triangleRoof(startx, starty, w, h, numAtt);
    else if(choice == 11)
      basic(startx, starty, w, h, numAtt);
    else if(choice == 12) {
      if(thinused)
        randomBuilding(startx, starty, w, h, numAtt);
      else
        thin(startx, starty, w, h, numAtt);
    }
    else
      var2(startx, starty, w, h, numAtt);
  }
  
  // is super tall
  else { 
    int choice = int(random(100));
    if(choice < 20)
      var1(startx, starty, w, h, numAtt);
    else if(choice < 40)
      var5(startx, starty, w, h, numAtt);
    else if(choice < 60)
      var7(startx, starty, w, h, numAtt);
    else if(choice < 80)
      stairstep(startx, starty, w, h, numAtt);
    else
      var8(startx, starty, w, h, numAtt);
  }
}

String[][] parseData(String texts) { 
  String[][] hourlyData = new String[25][3];
  String[] hours = texts.split("-");
  hourlyData[0] = new String[1];
  hourlyData[0][0] = hours[0];
  for(int i=1; i<hours.length; i++) { hourlyData[i] = hours[i].split("&"); } return hourlyData; } // ******************** VARIATION DESIGNS ********************* // void basic(float startx, float starty, float w, float h, int numAtt) { noStroke(); fill(0); rect(startx, starty, w, -1*h); if(numAtt>0)
    attGroup1(startx, starty, w, h, numAtt);
}

void slant(float startx, float starty, float w, float h, int numAtt) {
  int both = round(random(2));
  int left = round(random(1));
  float xr = random(w/6, w/3);
  float xl = random(w/6, w/3);
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(both==1) {
    vertex(startx+xl, starty-h);
    vertex(startx+w-xr, starty-h);
  }
  else {
    if(left==1) {
      vertex(startx+xl, starty-h);
      vertex(startx+w, starty-h);
    }
    else {
      vertex(startx, starty-h);
      vertex(startx+w-xr, starty-h);
    }
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt > 0) {
    if(both == 1)
      attGroup1(startx+xl, starty, w-(xr+xl), h, numAtt);
    else if(left == 1)
      attGroup1(startx+xl, starty, w-xl, h, numAtt);
    else
      attGroup1(startx, starty, w-xr, h, numAtt);
  }
}

void bevel(float startx, float starty, float w, float h, int numAtt) {
  int both = round(random(2));
  int left = round(random(1));
  float x1 = random(w/6, w/3); // width of slant
  float y1 = random(h/5); // height of slant
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(both==1) {
    vertex(startx, starty-h+y1);
    vertex(startx+x1, starty-h);
    vertex(startx+w-x1, starty-h);
    vertex(startx+w, starty-h+y1);
  }
  else {
    if(left==1) {
      vertex(startx, starty-h+y1);
      vertex(startx+x1, starty-h);
      vertex(startx+w, starty-h);
    }
    else {
      vertex(startx, starty-h);
      vertex(startx+w-x1, starty-h);
      vertex(startx+w, starty-h+y1);
    }
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt > 0) {
    if(both == 1)
      attGroup1(startx+x1, starty, w-x1*2, h, numAtt);
    else if(left == 1)
      attGroup1(startx+x1, starty, w-x1, h, numAtt);
    else
      attGroup1(startx, starty, w-x1, h, numAtt);
  }
}

void slice(float startx, float starty, float w, float h, int numAtt) {
  int left = round(random(1));
  float y1 = random(h/8, h/3); // height of slant
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(left==1) {
    vertex(startx, starty-h);
    vertex(startx+w, starty-h+y1);
  }
  else {
    vertex(startx, starty-h+y1);
    vertex(startx+w, starty-h);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+10+random(w-20), starty-h+y1, w, random(h/6, h/4), startx, starty, numAtt);
}

void angled(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/4, w-w/4); // corner point
  float yl = constrain(random(h/3),0,10); // height of left
  float yr = constrain(random(h/3),0,10); // height of right
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+yl);
  vertex(startx+x1, starty-h);
  vertex(startx+w, starty-h+yr);
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+10+random(w-20), starty-h+max(yl,yr), w, random(h/8, h/5), startx, starty, numAtt);
}

void stairstep(float startx, float starty, float w, float h, int numAtt) {
  int single = round(random(3));
  int left = round(random(1));
  float x1; //width of left
  float y1; //height of left
  if(single == 0) { // is both
    x1 = random(w/5, w/2);
    y1 = random(h/8, h/3);
  }
  else {
    x1 = random(w/4, w-w/4);
    y1 = random(h/8, h/3);
  }
  
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(single==0) {
    stairLeft(startx, starty, w, h, x1, y1);
    stairRight(startx, starty, w, h, x1, y1);
  }
  else if(left == 1) {
    stairLeft(startx, starty, w, h, x1, y1);
    vertex(startx+w, starty-h);
  }
  else {
    vertex(startx, starty-h);
    stairRight(startx, starty, w, h, x1, y1);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  numAtt = attGroup3(startx, starty, w, h-y1, numAtt);
  if(numAtt > 0) {
    if(single==0)
      attGroup0(startx, starty, w, numAtt);
    else if(left == 1)
      attGroup1(startx+x1, starty, w-x1, h, numAtt);
    else
      attGroup1(startx, starty, w-x1, h, numAtt);
  }
}

void stairLeft(float startx, float starty, float w, float h, float x1, float y1) {
  int steps = round(random(4,6));
  for(int i=0; i<=steps; i++) {
    float x = startx + ((x1/steps)*i);
    float y = (starty-h+y1) - ((y1/steps)*i);
    vertex(x,y);
    vertex(x+(x1/steps),y);
  }
}

void stairRight(float startx, float starty, float w, float h, float x2, float y2) {
  int steps = round(random(4,6));
  for(int i=0; i<steps; i++) {
    float x = startx + (w-x2) + ((x2/steps)*i);
    float y = (starty - h) + ((y2/steps)*i);
    vertex(x,y);
    vertex(x+(x2/steps),y);
  }
}

void blockRoof(float startx, float starty, float w, float h, int numAtt) {
  float y = random(h/2); // height of block
  if(y<20) y = 20; float space = random(5,13); //spacing between steps noStroke(); fill(0); beginShape(); vertex(startx, starty); vertex(startx, starty-h+y); vertex(startx+(w/space), starty-h+y); vertex(startx+(w/space), starty-h+y/2); vertex(startx+((w/space)*2), starty-h+y/2); vertex(startx+((w/space)*2), starty-h); //mid vertex(startx+w-((w/space)*2), starty-h); vertex(startx+w-((w/space)*2), starty-h+y/2); vertex(startx+w-(w/space), starty-h+y/2); vertex(startx+w-(w/space), starty-h+y); vertex(startx+w, starty-h+y); vertex(startx+w, starty); endShape(CLOSE); numAtt = attGroup3(startx, starty, w, h-y, numAtt); if(numAtt>0)
    attGroup2(startx+((w/space)*2)+5+random((startx+w-((w/space)*2))-(startx+((w/space)*2))-10), starty-h, w, random(h/6, h/4), startx, starty, numAtt);
}

void triangleRoof(float startx, float starty, float w, float h, int numAtt) {
  float y = random(h/15,h/5);
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y);
  vertex(startx+w/2, starty-h);
  vertex(startx+w, starty-h+y);
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+w/2, starty, w, h+random(h/6, h/4), startx, starty, numAtt);
}

void roundRoof(float startx, float starty, float w, float h, int numAtt) {
  float y = random(h/5);
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y);
  bezierVertex(startx, starty-h, startx+w/2, starty-h, startx+w/2, starty-h);
  bezierVertex(startx+w, starty-h, startx+w, starty-h+y, startx+w, starty-h+y);
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+5+random(w-10), starty, w, h+random(h/8, h/5), startx, starty, numAtt);
}

void oldRoof(float startx, float starty, float w, float h, int numAtt) {
  float x1 = overlap*0.75; //distance from edge
  float y1 = smallcap/4; //height
  float y2 = 3; //edge height
  if(h<=smallcap) { h = map(h, 0, smallcap, smallcap-smallcap/3, smallcap); float div = random(0.8, 1.8); startx += overlap*div; w -= (overlap*div)*2; x1 /= 3; y1 /= 3; y2 /= 3; } noStroke(); fill(0); beginShape(); vertex(startx, starty); vertex(startx, starty-h+y1-y2); vertex(startx+y2, starty-h+y1-y2); vertex(startx+y2, starty-h+y1); vertex(startx+x1, starty-h+y1); vertex(startx+x1, starty-h+y2); vertex(startx+x1-y2, starty-h); //mid vertex(startx+w-x1+y2, starty-h); vertex(startx+w-x1, starty-h+y2); vertex(startx+w-x1, starty-h+y1); vertex(startx+w-y2, starty-h+y1); vertex(startx+w-y2, starty-h+y1-y2); vertex(startx+w, starty-h+y1-y2); vertex(startx+w, starty); endShape(CLOSE); numAtt = attGroup3(startx, starty, w, h-y1, numAtt); if(numAtt > 0)
    attGroup4(startx, starty, w, h, numAtt);
}

void slantRoof(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(3,8); //horizontal size
  float y1 = random(3,15); //vertical size
  if(h<=smallcap) { h = map(h, 0, smallcap, smallcap-smallcap/3, smallcap); float div = random(0.8, 1.8); startx += overlap*div; w -= (overlap*div)*2; x1 /= 3; y1 /= 3; } noStroke(); fill(0); beginShape(); vertex(startx, starty); vertex(startx, starty-h+y1); vertex(startx-x1, starty-h); vertex(startx+w+x1, starty-h); vertex(startx+w, starty-h+y1); vertex(startx+w, starty); endShape(CLOSE); numAtt = attGroup3(startx, starty, w, h-y1, numAtt); } void thin(float startx, float starty, float w, float h, int numAtt) { thinused = true; float x1 = random(w/10, w/3); // start point of thin float x2 = random(w/2, w*.75); // width of thin float y1 = random(h/5, h/3); // height of first float y2 = random(h/4, h/2); // height of second noStroke(); fill(0); randomBuilding(startx+x1, starty, x2, h, numAtt); randomBuilding(startx-overlap/2, starty, overlap/2+x1+x2/2, y1, 0); randomBuilding(startx+x1+x2/2, starty, w-(x1+x2/2)+overlap/2, y2, 0); } // ******************** FULL DESIGNS ********************* // void var1(float startx, float starty, float w, float h, int numAtt) { startx -= overlap/3; w += (overlap/3)*2; float x1 = random(w/6, w/4); //slant width float y1 = random(h/4, h-h/4); //left slant start float y2 = random(20); //slant height float y1r = random(h/4,h-h/4); //right slant start int uneven = round(random(5)); noStroke(); fill(0); beginShape(); vertex(startx, starty); vertex(startx, starty-y1); vertex(startx+x1, starty-y1-y2); vertex(startx+x1, starty-h); vertex(startx+w-x1, starty-h); if(uneven > 0) {
    vertex(startx+w-x1, starty-y1r-y2);
    vertex(startx+w, starty-y1r);
  }
  else {
    vertex(startx+w-x1, starty-y1-y2);
    vertex(startx+w, starty-y1);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  attGroup1(startx+x1, starty, w-x1*2, h, numAtt);
}

void var2(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/6, w/4); //slant width
  float y1 = random(h-h/3); //bottom left slant start
  float y2 = random(20); //slant height
  float y1r = random(h-h/4); //bottom right slant start
  float y3 = random(h/3); //top height
  y1=constrain(y1,h/5,h-y3);
  y1r=constrain(y1,h/5,h-y3);
  int uneven = round(random(1));
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-y1);
  vertex(startx+x1, starty-y1-y2);
  vertex(startx+x1, starty-(h-y3-y2*2));
  vertex(startx+w/4, starty-(h-y3-y2));
  vertex(startx+w/4, starty-(h-y2));
    //mid
  vertex(startx+w/2, starty-h);
  vertex(startx+w/2+w/4, starty-(h-y2));
  vertex(startx+w/2+w/4, starty-(h-y3-y2));
  vertex(startx+w-x1, starty-(h-y3-y2*2));
  if(uneven == 1) {
    vertex(startx+w-x1, starty-y1r-y2);
    vertex(startx+w, starty-y1r);
  }
  else {
    vertex(startx+w-x1, starty-y1-y2);
    vertex(startx+w, starty-y1);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+w/2, starty-h, w, random(h/7, h/4), startx, starty, numAtt);
}

void var3(float startx, float starty, float w, float h, int numAtt) {
  var3used = true;
  startx -= overlap/2;
  w += overlap;
  float y1 = random(h/5, h/2); // start of curve
  float x1 = random(5,w/4); // gap
  int bridges = round(random(1,5));
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y1);
  bezierVertex(startx, starty-h, startx+(w-x1)/2, starty-h, startx+(w-x1)/2, starty-h);
  vertex(startx+(w-x1)/2, starty);
  endShape(CLOSE);
  beginShape();
  vertex(startx+w, starty);
  vertex(startx+w, starty-h+y1);
  bezierVertex(startx+w, starty-h, (startx+w)-(w-x1)/2, starty-h, (startx+w)-(w-x1)/2, starty-h);
  vertex((startx+w)-(w-x1)/2, starty);
  endShape(CLOSE);
  boolean top = true;
  for(int i=0; i<bridges; i++) { float y2 = random(h/2); //height on building float x3 = random(3,8); //thickness if(top) { y2 = h-y2; top = false; } else top = true; rect(startx+(w-x1)/2,starty-y2,x1,x3); } if(numAtt>0)
    attGroup0(startx, starty, w, numAtt);
}

void var4(float startx, float starty, float w, float h, int numAtt) {
  var4used = true;
  float left = round(random(1));
  float xl = random(w/4, w/2); // left "waist" amount
  float xr = random(w/4, w/2); // right "waist" amount
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(left == 1)
    vertex(startx+xl, starty-h/2);
  vertex(startx, starty-h);
  vertex(startx+w, starty-h);
  if(left == 0)
    vertex((startx+w)-xr, starty-h/2);
  vertex(startx+w, starty);   
  endShape(CLOSE);
  float weight = random(1.5,5);
  strokeWeight(weight);
  stroke(0);
  if(left==1)
    line(startx+weight, starty-weight, startx+weight, starty-h+weight);
  else
    line(startx+w-weight, starty-weight, startx+w-weight, starty-h+weight);
  
  if(numAtt>0)
    attGroup1(startx, starty, w, h, numAtt);
}

void var5(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/3, w*.75); //width of curve
  int left = round(random(1));
  w += x1/2; // *2?
  noStroke();
  fill(0);
  beginShape();
  if(left == 1) {
    startx -= x1/2;
    vertex(startx, starty);
    bezierVertex(startx+x1, starty, startx+x1, starty-h, startx+x1, starty-h);
    vertex(startx+w, starty-h);
    vertex(startx+w, starty);
  }
  else {
    vertex(startx+w, starty);
    bezierVertex(startx+w-x1, starty, startx+w-x1, starty-h, startx+w-x1, starty-h);
    vertex(startx, starty-h);
    vertex(startx, starty);
  }
  endShape(CLOSE);
  
  if(numAtt>0) {
    if(left == 1)
      attGroup1(startx+x1, starty, w-x1, h, numAtt);
    else
      attGroup1(startx, starty, w-x1, h, numAtt);
  }
}

void var7(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/10, w/3); // width of side
  float x2 = random(w/10, w/3);
  float y1 = random(h); // height of side
  float y2 = random(h);
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y1);
  vertex(startx+x1, starty-h);
  vertex(startx+w-x2, starty-h);
  vertex(startx+w, starty-h+y2);
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup1(startx+x1, starty, w-x1-x2, h, numAtt);
}

void var8(float startx, float starty, float w, float h, int numAtt) {
  float y1 = random(h-h/4, h); // height of angle corner
  float y2 = random(h/3, h/2); // height of start angle
  int left = round(random(1));
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(left == 1) {
    vertex(startx, starty-y2);
    vertex(startx+w/3, starty-y1);
    vertex(startx+w, starty-h);
  }
  else {
    vertex(startx, starty-h);
    vertex(startx+w-w/3, starty-y1);
    vertex(startx+w, starty-y2);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0) {
    if(left == 1)
      attGroup2(startx+5+w/3+random(w-w/3-5), starty-y1, w, random(h/5, h/3), startx, starty, numAtt);
    else
      attGroup2(startx+random(w-w/3-5), starty-y1, w, random(h/5, h/3), startx, starty, numAtt);
  }
}

void var9(float startx, float starty, float w, float h, int numAtt) {
  float origW = w;
  w = random(w-w/4, w);
  float y1 = random(8); // side slant height
  float y2 = 8; // top slant height
  float y3 = 10; // tower base height
  float x1 = w/3; // inward amount
  constrain(h, y1+y2+y3+5, smallcap);
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y2+y3+y1);
  vertex(startx+x1, starty-h+y2+y3);
  vertex(startx+x1, starty-h+y2);
  vertex(startx+w/2, starty-h);
    //mid
  vertex(startx+w-w/2, starty-h);
  vertex(startx+w-x1, starty-h+y2);
  vertex(startx+w-x1, starty-h+y2+y3);
  vertex(startx+w, starty-h+y2+y3+y1);
  vertex(startx+w, starty);
  endShape(CLOSE);
  fill(255);
  if(round(random(2)) > 0)
    ellipse(startx+w/2, starty-h+y2+y3/2, x1/3, x1/3);
    
  if(numAtt>0)
    attGroup0(startx, starty, origW, numAtt);
}

void var11(float startx, float starty, float w, float h, int numAtt) {
  float x1 = w/4; // width
  float x2 = random(3); // "wing"
  noStroke();
  fill(0);
  beginShape();
  vertex(startx+w/2-x1/2, starty);
  vertex(startx+w/2-x1/2, starty-h);
  vertex(startx+w/2-x1/2-x2, starty-h);
  vertex(startx+w/2, starty-h-random(4,10));
  vertex(startx+w/2+x1/2+x2, starty-h);
  vertex(startx+w/2+x1/2, starty-h);
  vertex(startx+w/2+x1/2, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup0(startx, starty, w, numAtt);
}

void var13(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/4,w); // width
  float x2 = random(3); // "wing"
  float y1 = random(4, 10);
  float start = startx + w/2;
  noStroke();
  fill(0);
  beginShape();
  vertex(start - x1/2, starty);
  vertex(start - x1/2, starty-h);
  vertex(start - x1/2 - x2, starty-h);
  vertex(start - x1/2 + x2*2, starty-h-y1);
  vertex(start + x1/2 - x2*2, starty-h-y1);
  vertex(start + x1/2 + x2, starty-h);
  vertex(start + x1/2, starty-h);
  vertex(start + x1/2, starty);
  endShape(CLOSE);
  
  if(round(random(1)) == 1) {
    beginShape();
    vertex(start - x2, starty-h-y1);
    vertex(start, starty-h-y1-y1/2);
    vertex(start + x2, starty-h-y1);
    endShape(CLOSE);
  }
  
  if(numAtt>0)
    attGroup0(startx, starty, w, numAtt);
}

// ******************** ATTACHMENT DESIGNS ********************* //

// limits to greenery
void attGroup0(float startx, float starty, float w, int numAtt) {
  while(numAtt>0) {
    tree(startx+random(w), starty);
    numAtt -= 10;
  }
}

// limits to blocks, antennas, greenery
void attGroup1(float startx, float starty, float w, float h, int numAtt) { //h = total height
  boolean antennaDone = false;
  while(numAtt > 0) {
    int doAntenna = round(random(1));
    int doSimple = round(random(1));
    if(numAtt>0 && doAntenna==1 && doSimple==0 && !antennaDone) { //two levels
      complexAntenna(constrain(startx+random(w),startx+10, (startx+w)-10), starty-h, random(h/7, h/4));
      numAtt -= 10;
      antennaDone = true;
    }
    if(numAtt>0 && doAntenna==1 && !antennaDone) {
      antenna(startx+5+random(w-10), starty-h, random(h/9, h/5));
      numAtt -= 10;
      antennaDone = true;
    }
    if(numAtt>0) {
      block(startx, startx+w, starty-h);
      numAtt -= 10;
    }
    //draw greenery, last resort
  }
}

// limits to single antenna starting at various heights (calculated beforehand), then greenery
void attGroup2(float startx, float starty, float w, float h, float treeStartx, float treeStarty, int numAtt) {
  antenna(startx, starty, h);
  numAtt -= 10;
  if(numAtt>0)
    attGroup0(treeStartx, treeStarty, w, numAtt);
}

// draws balconies alongside the two vertical edges, returns adjusted numAtt value
int attGroup3(float startx, float starty, float w, float h, int numAtt) {
  if(numAtt > 10) {
    balcony(startx, starty, h);
    balcony(startx+w, starty, h);
    return numAtt - 20;
  }
  return numAtt;
}

// limits to blocks, dishes, watertowers, and greenery
void attGroup4(float startx, float starty, float w, float h, int numAtt) { //h = total height
  boolean towerDone = false;
  boolean dishDone = false;
  while(numAtt > 0) {
    int doTower = round(random(5));
    int doDish = round(random(5));
    if(!towerDone && doTower == 1) {
      watertower(startx+5+random(w-25), starty-h, constrain(map(h,0,medcap,.1,.9), .1, .9));
      numAtt -= 10;
      towerDone = true;
    }
    if(!dishDone && doDish == 1 && numAtt > 0) {
      dish(startx+15+random(w-30), starty-h, constrain(map(h,0,medcap,.5,1), .5, 1));
      numAtt -= 10;
      dishDone = true;
    }
    if(numAtt>0) {
      block(startx+5, startx+w-10, starty-h);
      numAtt -= 10;
    }
    //draw greenery, last resort
  }
}

void antenna(float startx, float starty, float h) {
  strokeWeight(random(1.5,3));
  stroke(0);
  line(startx, starty, startx, starty-h);
}

void complexAntenna(float startx, float starty, float h) {
  float x1 = random(3,8); // distance between beams
  float y1 = random(h/7, h-h/4); // height of secondary beam
  float y2 = random(y1); //connector beam heights
  float y3 = random(y1);
  if(round(random(1))==1) x1 *= -1;
  strokeWeight(random(1.5,3));
  stroke(0);
  line(startx, starty, startx, starty-h);
  strokeWeight(random(1.5,3));
  line(startx+x1, starty, startx+x1, starty-y1);
  line(startx, starty-y2, startx+x1, starty-y2);
  line(startx, starty-y3, startx+x1, starty-y3);
}

void block(float startx, float endx, float starty) {
  float w = random(8, (endx-startx)-(endx-startx)/4); //width
  float h = -1*random(4, w/2); //height
  float x1 = random(startx, endx-w); //start position
  noStroke();
  fill(0);
  rect(x1, starty, w, h);
}

void balcony(float startx, float starty, float hval) {
  int amount = int(map(hval, 0, tallcap, 0, 40));
  float w = 8;
  float h = w/2;
  float x1 = random(w/3,w/2);
  noStroke();
  fill(0);
  for(int i=0; i<amount; i++) {
    rect(startx-x1, starty-(hval/amount)*i-h, w, h);
  }
}

void watertower(float startx, float starty, float scale) {
  float h = 22*scale;
  float w = 15*scale;
  float y1 = h/5.5; // barrel start
  float y2 = h/10; // barrel roof height
  float x1 = w*.8; // barrel width
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty-y1);
  vertex(startx, starty-h+y2);
  bezierVertex(startx, starty-h,startx+x1/2, starty-h,startx+x1/2, starty-h);
  bezierVertex(startx+x1, starty-h,startx+x1, starty-h+y2,startx+x1, starty-h+y2);
  vertex(startx+x1, starty-y1);
  endShape(CLOSE);
  strokeWeight(2*scale);
  stroke(0);
  line(startx, starty, startx+w*.1, starty-y1);
  line(startx+x1, starty, (startx+x1)-w*.1, starty-y1);
  line(startx+x1/2, starty, startx+x1/2, starty-y1);
  strokeWeight(1.5*scale);
  line(startx+w, starty, startx+w, starty-h+y2);
  line(startx+w, starty-h+y2*2, startx+w-w*.3, starty-h+y2*2);
}

void dish(float startx, float starty, float scale) {
  float side = 25*scale;
  noFill();
  strokeWeight(2*scale);
  stroke(0);
  if(round(random(1)) == 1) {
    arc(startx, starty-side/2-5, side, side, PI/2, PI);
    line(startx-side*.35, starty, startx-side*.35, starty-side*.35);
    line(startx-side*.35, starty-side*.35, startx-side*.35+side*.2, starty-side*.35-side*.2);
  }
  else {
    arc(startx, starty-side/2-5, side, side, 0, PI-PI/2);
    line(startx+side*.35, starty, startx+side*.35, starty-side*.35);
    strokeWeight(1.5*scale);
    line(startx+side*.35, starty-side*.35, startx+side*.35-side*.2, starty-side*.35-side*.2);
  }
}

void tree(float startx, float starty) {
  int treeIndex = int(random(trees.size()));
  //int y = round(random(25, 50));
  PImage tree = trees.get(treeIndex);
  //tree.resize(0,y);
  image(tree, startx-tree.width/2, starty-tree.height+1);
}

 


Processing: Modified version of Justin Livi’s code for generating watercolor paintings

/*
 * WatercolorSediment
 * May, 2011
 *
 * Copyright 2011  Justin Livi
 * justinlivi.net
 * Written in Processing
 *
 */

int seedcount = 10; // default = 50
float h, maxh, start; // overall distance from center
float vspeed, rspeed;
float theta = 0;
float speed = 1;
float rot;
boolean up = false;
float[] hm; // hue array
float[] sm; // saturation array
float[] lm; // lightness array
float[] dhm; // delta hue array
float[] dsm; // delta saturation array
float[] dlm; // delta lightness array
float[] dm; // distance array
float[] stm; // streakiness array
HSL hsl = new HSL();

int count = 0;
boolean a = true;
boolean pause;

void setup() {
  size(1700, 1700);
  smooth();
  noStroke();
  background(255);
  if(width > height)
    maxh = height;
  else
    maxh = width;
  hm = new float[seedcount];
  sm = new float[seedcount];
  lm = new float[seedcount];
  dhm = new float[seedcount];
  dsm = new float[seedcount];
  dlm = new float[seedcount];
  dm = new float[seedcount];
  stm = new float[seedcount];
  reset();
}

void draw() {
  if(pause) { return; }
  if (theta < width) {
    pushMatrix();
      translate(theta, 0);
      rotate(PI/2);
      generate();
    popMatrix();
    theta++;
  }
  else if(count<400) {
    if(a) {
      saveFrame("paintings/painting-"+count+"_a.png");
      a = false;
    }
    else {
      saveFrame("paintings/painting-"+count+"_b.png");
      a = true;
      count++;
    }
    reset();
  }
}

void mousePressed() {
  pause = !pause;
  //background(255);
  //reset();
}

void reset() {
  seedcount = (int)random(8, seedcount); 
  float centerhue = random(0, 360);
  float variance = random(10, 40);
  for(int count = 0; count < seedcount; count++) {
    hm[count] = random(centerhue-variance, centerhue+variance);
    sm[count] = random(45, 70);
    lm[count] = random(20, 90);
    dhm[count] = hm[count];
    dsm[count] = sm[count];
    dlm[count] = lm[count];
    dm[count] = random(maxh);
    stm[count] = random(.1, 1);
  }
  dm[0] = 0;
  dm[seedcount-1] = height;
  dm = sort(dm, seedcount);
  h = 0;
  start = h;
  theta = -20;
  vspeed = random(1, 9);
  rspeed = random(1, 4);
}

void generate() {
  for(int count = 0; count < seedcount; count++) { changeDm(count); fill(hsl.toRGB(dhm[count],dsm[count],dlm[count],100)); blend(count, dm[count]); } dm = sort(dm, seedcount); } // ---------------------- POSITION! ------------------------------------ // void changeDm(int count) { int prev = 0; int next = seedcount-1; if(count > 0)
    prev = count-1;
  if(count < seedcount-1)
    next = count+1;
  if(count == seedcount-1)
    prev = seedcount-1;
  if(count == 0)
    next = 0;
  dm[count] = constrain(dm[count]+random(-1, 1), 0, height);
  if (count == 0)
    dm[count] = 0;
  else if (count == seedcount-1)
    dm[count] = height;
}


// ---------------------- BLEND! ------------------------------------ // 

void blend(int count, float distance) {
  int prev = seedcount-1;
  if(count < seedcount-1)
    prev = count+1;
  float formax = abs(dm[prev]-distance);
  changeColor(count);
  for(int count2 = 0; count2 < formax; count2++) {
    for(int count3 = 0; count3 < (10/3.0*stm[count]+5/3.0); count3++) {
      float hi = (dhm[prev]-dhm[count])/formax;
      float si = (dsm[prev]-dsm[count])/formax;
      float li = (dlm[prev]-dlm[count])/formax;
      fill(hsl.toRGB(dhm[count]+random(-1,1)+(count2*hi),
                    dsm[count]+random(-.5,.5)+(count2*si),
                    dlm[count]+random(-.5,.5)+(count2*li), random(2, 10)));
      pushMatrix();
        translate(distance+count2+random(-1,1), random(-stm[count]*10,stm[count]*10));
        rotate(random(PI*2));
        ellipse(0, 0, random(2, 10+5*stm[count]), random(2, 10+5*stm[count]));
      popMatrix();
    }
  }
}

void changeColor(int count) {
  stm[count] += random(-.01, .01);
  stm[count] = constrain(stm[count], .1, 1);
  dhm[count] += random(-stm[count], stm[count]);
  dhm[count] = constrain(dhm[count], hm[count]-20, hm[count]+20);
  dsm[count] += random(-stm[count], stm[count]);
  dsm[count] = constrain(dsm[count], sm[count]-20, sm[count]+20);
  dlm[count] += random(-stm[count], stm[count]);
  dlm[count] = constrain(dlm[count], lm[count]-20, lm[count]+20);
}

class HSL {
  color toRGB(float H, float S, float L) {
    float R = 0, G = 0, B = 0;
    H /= 360;
    S /= 100;
    L /= 100;
    float temp1 = 0, temp2 = 0, Rtemp3 = 0, Gtemp3 = 0, Btemp3 = 0;
    if (S == 0) {
      R = L;
      G = L;
      B = L;
    }
    else {
      if (L < 0.5) temp2 = L*(1.0+S); else if (L >= 0.5)
        temp2 = L+S-L*S;
      temp1 = 2.0*L-temp2;
      Rtemp3 = H+1.0/3.0;
      
      if (Rtemp3 < 0) Rtemp3 = Rtemp3 + 1.0; if (Rtemp3 > 1)
        Rtemp3 = Rtemp3 - 1.0;
        
      Gtemp3 = H;
      if (Gtemp3 < 0) Gtemp3 = Gtemp3 + 1.0; if (Gtemp3 > 1)
        Gtemp3 = Gtemp3 - 1.0;
        
      Btemp3 = H-1.0/3.0;
      if (Btemp3 < 0) Btemp3 = Btemp3 + 1.0; if (Btemp3 > 1)
        Btemp3 = Btemp3 - 1.0;
        
      if (6.0*Rtemp3 < 1)
        R = temp1+(temp2-temp1)*6.0*Rtemp3;
      else if (2.0*Rtemp3 < 1)
        R = temp2;
      else if (3.0*Rtemp3 < 2)
        R = temp1+(temp2-temp1)*((2.0/3.0)-Rtemp3)*6.0;
      else
        R = temp1;
        
      if (6.0*Gtemp3 < 1)
        G = temp1+(temp2-temp1)*6.0*Gtemp3;
      else if (2.0*Gtemp3 < 1)
        G = temp2;
      else if (3.0*Gtemp3 < 2)
        G = temp1+(temp2-temp1)*((2.0/3.0)-Gtemp3)*6.0;
      else
        G = temp1;
        
      if (6.0*Btemp3 < 1)
        B = temp1+(temp2-temp1)*6.0*Btemp3;
      else if (2.0*Btemp3 < 1)
        B = temp2;
      else if (3.0*Btemp3 < 2)
        B = temp1+(temp2-temp1)*((2.0/3.0)-Btemp3)*6.0;
      else
        B = temp1;
    }
      
    R *= 255;
    B *= 255;
    G *= 255;
    
    return color((int)R, (int)G, (int)B);
  }
  
  color toRGB(float H, float S, float L, float A) {
    float R = 0, G = 0, B = 0;
    H /= 360;
    S /= 100;
    L /= 100;
    float temp1 = 0, temp2 = 0, Rtemp3 = 0, Gtemp3 = 0, Btemp3 = 0;
    if (S == 0) {
      R = L;
      G = L;
      B = L;
    }
    else {
      if (L < 0.5) temp2 = L*(1.0+S); else if (L >= 0.5)
        temp2 = L+S-L*S;
      temp1 = 2.0*L-temp2;
      Rtemp3 = H+1.0/3.0;
      
      if (Rtemp3 < 0) Rtemp3 = Rtemp3 + 1.0; if (Rtemp3 > 1)
        Rtemp3 = Rtemp3 - 1.0;
        
      Gtemp3 = H;
      if (Gtemp3 < 0) Gtemp3 = Gtemp3 + 1.0; if (Gtemp3 > 1)
        Gtemp3 = Gtemp3 - 1.0;
        
      Btemp3 = H-1.0/3.0;
      if (Btemp3 < 0) Btemp3 = Btemp3 + 1.0; if (Btemp3 > 1)
        Btemp3 = Btemp3 - 1.0;
        
      if (6.0*Rtemp3 < 1)
        R = temp1+(temp2-temp1)*6.0*Rtemp3;
      else if (2.0*Rtemp3 < 1)
        R = temp2;
      else if (3.0*Rtemp3 < 2)
        R = temp1+(temp2-temp1)*((2.0/3.0)-Rtemp3)*6.0;
      else
        R = temp1;
        
      if (6.0*Gtemp3 < 1)
        G = temp1+(temp2-temp1)*6.0*Gtemp3;
      else if (2.0*Gtemp3 < 1)
        G = temp2;
      else if (3.0*Gtemp3 < 2)
        G = temp1+(temp2-temp1)*((2.0/3.0)-Gtemp3)*6.0;
      else
        G = temp1;
        
      if (6.0*Btemp3 < 1)
        B = temp1+(temp2-temp1)*6.0*Btemp3;
      else if (2.0*Btemp3 < 1)
        B = temp2;
      else if (3.0*Btemp3 < 2)
        B = temp1+(temp2-temp1)*((2.0/3.0)-Btemp3)*6.0;
      else
        B = temp1;
    }
      
    R *= 255;
    B *= 255;
    G *= 255;
    
    return color((int)R, (int)G, (int)B, (int)A);
  }
}

 


Processing: Combining city masks and paintings to create final images

PImage painting;
PImage drawing;
int index = 17;
int savedIndex = -1;
String file = "../Watercolor_JustinLivi/paintings/painting-"+int(random(400))+"_a.png";

ArrayList used = new ArrayList();

void setup() {
  size(1700,1700);
  reload();
}

void draw() {
}

void keyPressed() {
  if (key == 'b' || key == 'B') {
    index--;
    if(index<0) index = 365; reload(); } if (key == 'n' || key == 'N') { index++; if(index >= 366) index = 0;
    reload();
  }
  if (key == 's' || key == 'S') {
    String filename = "renders/render"+index+".png";
    saveFrame(filename);
    if(savedIndex == index)
      used.remove(index);
    used.add(index, file);
    savedIndex = index;
  }
  if (key == 'r' || key == 'R') {
    reload();
  }
}

void reload() {
  background(255);
  String prevFile = file;
  while(used.contains(file) || prevFile.equals(file)) {
    if(round(random(1)) == 1)
      file = "../Watercolor_JustinLivi/paintings/painting-"+int(random(400))+"_a.png";
    else 
      file = "../Watercolor_JustinLivi/paintings/painting-"+int(random(400))+"_b.png";
  }
  println("drawing"+index+".png + "+ file);
  painting = loadImage(file);
  drawing = loadImage("../city_generator/drawings/drawing"+index+".png");
  drawing.filter(INVERT);
  painting.mask(drawing);
  image(painting, 0, 0);
}

 

Javascript/Basil.js: Create final Indesign/PDF file

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";

var StringData = b.loadString("data.txt");
var data = b.loadString('data.txt').split("\n");

var side = 7.5*72;
var img;

function parseData(texts) { 
  var hourlyData = [];
  var hours = texts.split("-");
  hourlyData.push([hours[0]]);
  for(var i=1; i<hours.length; i++)
    hourlyData.push(hours[i].split("&"));
  return hourlyData;
}

function getMetaData(hourlyData){
  var info = [];
  info.push(hourlyData[0][0]);
  var texts = 0;
  var chars = 0;
  var atts = 0;
  for(var i=1; i<hourlyData.length; i++) {
    texts += parseInt(hourlyData[i][0]);
    chars += parseInt(hourlyData[i][1]);
    atts += parseInt(hourlyData[i][2]);
  }
  info.push(texts);
  info.push(chars);
  info.push(atts);
  return info;
}

function setup() {

  b.clear (b.doc());
  
  b.noStroke(); 
  img = b.image("dedication_s.png", 0, 0, side, side);
  b.fill(255,255,255);
  b.textSize(12);
  b.textFont("Avenir","Light"); 
  b.textAlign(Justification.CENTER_ALIGN); 
  b.text("for nicole", 0, side/2-8, side, 16);

  // b.fill(175,175,175);
  // b.textSize(13);
  // b.text("special thanks to", 0, side/2-58, side, 16);
  // b.textSize(10);
  // b.text("golan levin", 0, side/2-8, side, 16);
  // b.text("justin livi", 0, side/2+12, side, 16);
  // b.text("adam knuckey", 0, side/2+32, side, 16);

  return

  for(var i=0; i<data.length; i++) {
    b.addPage();

    var date = getMetaData(parseData(data[i]))[0].split(",")[0].split(" ");
    var date_space = 10;
    
    b.fill(175,175,175);
    var tbh = 30;
    b.textSize(tbh-5);
    b.textFont("Avenir","Light"); 
    b.textAlign(Justification.RIGHT_ALIGN);

    b.text(date[0].toUpperCase().substring(0,3), 0, side/2-tbh/2, side/2-date_space/2, tbh);
    b.textFont("Avenir","Black"); 
    b.textAlign(Justification.LEFT_ALIGN);
    b.text(date[1], side/2+date_space/2, side/2-tbh/2, side/2, tbh);

    var y = side-72;
    var iconHeight = 15;
    var chatWidth = iconHeight*1.6;
    var leftSpace = 5;
    var typeX = chatWidth+leftSpace+chatWidth*1.5;
    var clipX = typeX+chatWidth+leftSpace+chatWidth*2;
    var totalWidth = clipX + chatWidth*0.55 + leftSpace + chatWidth*2;
    var x = side/2-totalWidth/2;

    b.textSize(10);
    b.fill(220,220,220);
    b.textFont("Avenir","Black"); 

    img = b.image("chat.png", x, y, chatWidth, iconHeight);
    b.text(getMetaData(parseData(data[i]))[1], x+chatWidth+leftSpace, y+1.5, chatWidth*1.5, 15);
    img = b.image("type.png", x+ typeX, y+1.5, chatWidth, iconHeight*.8);
    b.text(getMetaData(parseData(data[i]))[2], x+typeX+chatWidth+leftSpace, y+1.5, chatWidth*2, 15);
    img = b.image("clip.png", x+ clipX, y+1.5, chatWidth*0.55, iconHeight*.8);
    b.text(getMetaData(parseData(data[i]))[3], x+clipX+chatWidth*0.55+leftSpace, y+1.5, chatWidth*2, 15);


    b.addPage();

    img = b.image("renders/render"+i+".png", 0, 0, side, side);
  }
}

b.go(); 

Written by Comments Off on hizlik-book Posted in Book

Catlu – Book

MythMash is a whimsical mashup of the styles of major world mythologies. It pulls from a list of 9 Mythologies: African, Arabian, Asian, Celtic, Classical, Egyptian, Native American, Icelandic, and Polynesian. Each page contains a small myth generated using Markov chains and 2 random mythologies. The idea is to think about the place of stories and mythology in the world. The different mythologies all have many similarities, while also retaining individuality and cultural flavor. I encourage the reader to think about this while enjoying fun and entertaining newly generated stories.

20161028_004932

The idea of this book came to me as I was thinking about how to incorporate something I really loved and cared about into this generative book project. I think mythology is fascinating, beautiful, and amazing in scope. As I was thinking about it, I thought about how certain stories carry across cultures and regions: hero stories, creation myths, god figures, etc. This made me think about how storytelling is universal in the world, and is a vessel for creativity and empathy. Eventually, I decided to mash mythology styles together to show these similarities in an interesting and whimsical way, while hoping they still retained enough individual flavor to make them recognizable within the whole. Starting this book, I read a bit on Markov code. Eventually I decided to use RiTa.js’ Markov functions. For each mythology, I went through online sources and our school library to find texts of myths, and compiled about 60 pages in MS Word for each. I made it so that my program would randomly select two different mythologies for each page and mash them together. For pictures to accompany the words, at first I thought I could use patterns (also universal), but Golan suggested this could be cliche and I agreed. He then suggested that I choose random images that had to do vaguely with my myths, and that this might add a humorous and mysterious element. I liked the idea so Golan gave my a file with 1 million Flickr photos and their captions. With these pictures, I picked a random noun from the first 15 or so words, and looked for it in the captions that Golan provided me, saving everything to a JSON. I had a lot of problems with the million image file, which took 2 hours to transfer every time I had to move it. In the million images, a small percentage of them were missing (and way more missing photos got pulled than I really expected), and I had to go in and manually add existent images in when I was importing them into inDesign. Eventually, as I was checking through, I found out that some of the caption numbers were offset by 1, while others were not, and that this was completely random. Very frustrated at this point, I really looked at the pictures and decided they weren’t relevant enough to really be all that funny or merit being in the book. In the end, it turned out to be just the text. I was very excited about this project to be begin with, and wish it had turned out a little better. I couldn’t find how to make my own Markov code so had to use RiTa’s with little choice of personalization. Dan Shiffman released videos explaining how to write Markov when I was rushing and struggling to get the pictures to work with the Markov I already had. As for the pictures, I spent maybe 25-30 hours trying to transfer the files several times(8 hours), figure out the code, and work out all the little insidious bugs and how to deal with all the shifting zeros in the 1000 folders of 1000 pictures in the million file. In the end, I was very disappointed that I didn’t love the effect of the pictures as much as I would have liked, and that I spent so long on them and didn’t use them. I definitely really like the idea of the generative book, and how the myths came out, but I may revisit this project again when I have more time to really invest in getting everything right. The content of this book is important to me, and in the future I definitely plan on coming back and making the book every bit as good as it can be, writing the Markov myself and thinking through the right way to illustrate the myths.

MythMash PDF:

mythMash2.pdf

The code can be found in the below Github links:
Github Code:

Processing:

https://github.com/catlu4416/60-212/blob/master/markov4.pde

import processing.pdf.*;
import rita.*;


String captions[];

int rand1;
int rand2;
String myth1;
String myth2;

String mythsTitlePart1;
String mythsTitlePart2; 
String mythsTitle;

//lexicon initialization
RiLexicon lexicon = new RiLexicon();

//markov initialization
RiMarkov markov;
String mythText = "click to (re)generate!";
int x = 160, y = 240;

//array of myth types
String[] mythTypes = {"africanMyths.txt", "nativeAmericanMyths.txt", "asianMyths.txt", "arabianMyths.txt", 
  "celticMyths.txt", "egyptianMyths.txt", "norseIcelandicMyths.txt", "polynesianMyths.txt", "classicalMyths.txt"};

String[] mythTypesText = {"African", "Native American", "Asian", "Arabian", 
  "Celtic", "Egyptian", "Icelandic", "Polynesian", "Classical"};


//caption stuff
String captionPull;
//caption stuff try 2
String mythCaption1;
String mythCaption2;

String searchNoun;

int foundCount = 0;
IntList picNumbers;

//JSON stuff
JSONArray bookStuff;
String jsonFinal;


void setup()
{
  size(500, 800);

  beginRecord(PDF, "everything.pdf");

  fill(0);
  textFont(createFont("times", 16));
  bookStuff = new JSONArray();
}

void captions() {
  //caption stuff//Golan

  picNumbers = new IntList();

  String captionsFilename = "SBU_captioned_photo_dataset_captions.txt"; 
  String captions[] = loadStrings(captionsFilename); 

  int answerCount = 0; 
  String wordsIWant[] = { searchNoun }; 
  for (int i=0; i

Basil.js JavaScript for InDesign (.jsx):

https://github.com/catlu4416/60-212/blob/master/myBook.jsx

#includepath "~/Documents/;%USERPROFILE%Documents";
#includepath "D:\\Documents";
#include "basiljs/bundle/basil.js";

// Load a data file containing your book's content. This is expected
// to be located in the "data" folder adjacent to your .indd and .jsx. 
var jsonString = b.loadString("book.json");
var jsonData;
var imageFolder;
var anImageFilename;
var anImage;

//--------------------------------------------------------
function setup() {

  // Clear the document at the very start. 
  b.clear (b.doc());
  
  // Make a title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Garamond"); 
  b.textAlign(Justification.CENTER_ALIGN); 
  b.text("MythMash", 72,72,360,36);
  b.textSize(16);
  b.text("Is a journey into the many mythologies of our world; a whimsical examination of their similarities and differences through generative mashup myth.", 72,108,360,300);

  
  // Parse the JSON file into the jsonData array
  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);
  b.println(jsonData);


  // Initialize some variables for element placement positions.
  // Remember that the units are "points", 72 points = 1 inch.
  var titleX = 320; 
  var titleY = 415;
  var titleW = 150;
  var titleH = 50;

  var captionX = 54; 
  var captionY = 54;
  var captionW = 396;
  var captionH =396;

  var imageX = 72; 
  var imageY = 72-30; 
  var imageW = 72*3; 
  var imageH = 72*3;


  // Loop over every element of the book content array
  // (Here assumed to be separate pages)
  for (var i = 0; i < jsonData.length; i++) {

    // Create the next page. 
    b.addPage();

    // Load an image from the "images" folder inside the data folder;
    // Display the image in a large frame, resize it as necessary. 
    b.noStroke();  // no border around image, please.
    
    //6 digits (hundreds of thousands)
    if (b.floor((jsonData[i].image/1000000)) == 0) {
        imageFolder = "00" + b.floor((jsonData[i].image/1000));
    }
    //5 digits (tens of thousands)
    if (b.floor((jsonData[i].image/100000)) == 0) {
        imageFolder = "000" + b.floor((jsonData[i].image/1000));
    }
    //4 digits (thousands)
    if (b.floor((jsonData[i].image/10000)) == 0) {
        imageFolder = "0000" + b.floor((jsonData[i].image/1000));
    }
    //first 1000
    if (b.floor((jsonData[i].image/1000)) == 0) {
        imageFolder = "0000" + b.floor((jsonData[i].image/10000));
    }

    
    
    var anImageFilename = "images/" + imageFolder + "/" + jsonData[i].image + ".jpg";
    var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
    anImage.fit(FitOptions.PROPORTIONALLY);
   
    

    // Create textframes for the "title" field.
    // Draw an ellipse with a random color behind the title letter.
    //b.noStroke(); 
    //b.fill(b.random(180,220),b.random(180,220),b.random(180,220)); 
    //b.ellipseMode(b.CORNER);
    //b.ellipse (titleX,titleY,titleW,titleH);
    
    b.addPage();
    
    b.fill(0);
    b.textSize(10);
    b.textFont("Garamond"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text(jsonData[i].title, titleX,titleY,titleW,titleH);
    //b.println(jsonData[i].title);

    // Create textframes for the "caption" fields
    b.fill(0);
    b.textSize(12);
    b.textFont("Garamond"); 
    b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN );
    b.text(jsonData[i].caption, captionX,captionY,captionW,captionH);

  };
}

// This makes it all happen:
b.go(); 


Here's a video of Golan flipping through my book:

Written by Comments Off on Catlu – Book Posted in Book

Keali-Book

cover

****[Eng Sub] by HorribleSubs (but actually though…) is a collection of randomly selected anime episode screencaps mismatched with arbitrarily generated, nonsensical, and irrelevant subtitles created through Markov Chains. A light-hearted and comedic production, the book pays homage to the dear memories of classic Japanese animated shows around which my adolescent life revolved; the collection of such franchises was a big influence on me wanting to become an artist. The title is reminiscent of the general title formats of the videos, as non-Japanese speakers attentively sought for the episodes that had the [eng sub] tag in the search results. *HorribleSubs is a popular provider of such subtitled episodes for numerous seasons.

Process:
My initial idea was entirely different–in retrospect, it’s amusing that my ideas throughout the process and the final resulting product are both appealing aesthetics to me, though visually and thematically different. I originally intended to make my book using APIs regarding nature astronomy–I thus researched Google APIs, particularly those affiliated with Google Maps and Google Earth (which favorably extended to data of oceans, landmarks, and stars). I wanted to randomly generate latitude and longitude locations on the planet, and then visually represent the state of the sky, and possibly the surroundings, at that location using designs and typography. I sketched out some visuals, having different fonts and sizes for stars and planets being laid onto the page depending on the angle point of view and the distance of the celestial bodies and Earth. This plan eventually fell farther from my grasp as I failed to find the relevant APIs, and also discovered that many of the astronomy-related APIs were no longer updated or accessible.

It was then when I noticed that plenty of the generative book type arts I had seen up to that point were comedic, meant to be a light and comical read, and I thought perhaps I could attempt this as well being an illustrator that usually does not work on comedy-related artworks. As such came my anime idea, where I just had the most arbitrary thought of pairing up random screencaps of episodes to nonsensical, maybe gibberish, captions. I laughed at the thought of the most ordinary images captioned with the most irrelevant subtitles underneath. This also meant a lot to me personally as someone who decided to pursue art because of the cartoons and games with which I grew up. It was refreshing to, in a way, revisit and work on a project that displayed this field of art which, to my own experience, has been such a taboo at this institution for reasons I cannot comprehend. (Please take cartoons seriously…) I feel like it has been so long since I’ve made something this potentially funny and light-hearted.

The main key in getting started was familiarizing myself with Rita–something completely new: I needed to learn about Markov Chains. This meant Dan Shiffman tutorials, dissecting references, and downloading and analyzing the example code that generated random sentences whenever the user clicked. Essentially, I modified the example code to feed in my own text files (collected anime subtitles), and to output the JSON objects as an array with {“subtitle”:} types.

I then wrote a script in Python to take and save random screenshots per all the downloaded episode files, and to save and condense all the subtitles per video into .txt files, organized by series. I brought all the series’ subtitles into the Processing file to generate sentences using Markov from them. I then ran the program and clicked as many times as I wanted to collect plenty randomly generated, nonsense subtitles that would be placed in my book.

Bringing the code into basil was more bearable with the sample book which Golan provided; the necessary lines and files were replaced with my own, and from there on the priority was to mimic the aesthetic of an adolescent’s exposure to anime: I wanted to the book to be unrefined, simple, informal. The screencaps were aligned per page in the center, as if it was directly captured from the video itself, and the subtitles were positioned to be at the bottom of the screen, also center-aligned and not past the video borders. The subtitle font was purposely chosen to be of the ones typically used to sub episodes, with easy readability, reasonable size, and a white or pale yellow fill color. I wanted to represent the online streaming environment as much as possible to fulfill the vision of the book (as direct representations of a subbed episode on some browser video player).

Streaming the episodes of a foreign cartoon from the states actually felt risky at times; if you couldn’t find the episode you wanted on youtube, you had to rely on google, and I personally was always wary with which sites could be trusted/which one had reliable subtitles (haha)/which one had good quality/which one may give my computer a virus, etc… I also factored this “experience” into the overall visual aspect of the book: once again, childish, innocent, perhaps even ratchet–the outcome is simple with minimal elements, but I feel like it fulfilled the task of essentially representing the necessary attributes. The final result made me, and others, laugh–which I am more than thankful for. I would also say the simple outcome is deceptively simple from all the generativity behind it…(personal benchmark: first time writing a script…!)

process

picmonkey-collage

flipbookgif

Here’s a video of the professor, flipping through the book:

final pdf: kearniebook5

img_4015

//GitHub repository

Python script:

import subprocess
import shutil
import shlex
import re
import ass 
import os
def takeSnapshots(fileName,ep):
    amountPerEp = 10
    episodeTime = 20*60
    ignoreTime = 60*3
    interval = int((episodeTime - 2*ignoreTime)/amountPerEp - 0.01)
    base = "kearnie/screencaps/"
    extractTimes = [i for i in range(ignoreTime,episodeTime-ignoreTime,interval)]
    for i in range(len(extractTimes)):
        time = extractTimes[i]
        args = ["mpv","-ao","null","-sid","no","-ss",str(int(time)),"-frames","1","-vo","image",
        "--vo-image-format=png", fileName]
        try:
          subprocess.run(args)
          shutil.move("00000001.png",base + "%d.png" % (ep*amountPerEp+i))
        except:
          print("fal")
trackRegex = re.compile("mkvextract:\s(\d)")
removeBrackets = regex = re.compile(".*?\((.*?)\}")
def getSubtitleTracks(fileName):
    output = subprocess.check_output(["mkvinfo",fileName],universal_newlines=True).splitlines()
    currentTrack = None
    sub_tracks = []
    for line in output:
        if "Track number:" in line:
            trackNumber = trackRegex.search(line).group(1)
            currentTrack = trackNumber
        if "S_TEXT/ASS" in line:
            sub_tracks.append(currentTrack)
    return sub_tracks
def exportSRT(fileName, track):
    srtName = fileName + "-%s.srt" % track
    args = ["mkvextract", "tracks",fileName, "%s:%s" % (track,srtName)]
    subprocess.run(args)
    return srtName
def cleanLine(line):
    newLine = ""
    inBracket = False
    lastBackSlash = False
    for c in line:
        if c == "{":
            inBracket = True
        elif c == "}":
            inBracket == False
        elif not inBracket:
            if c == "\\":
                lastBackSlash = True
            elif c != "N" or not lastBackSlash:
                newLine += c
                lastBackSlash = False
    return newLine

def extractTextFromSubtitles(fileName):
    tracks = getSubtitleTracks(fileName)
    output = ""
    for track in tracks:
        srtName = exportSRT(fileName, track)
        lines = []
        with open(srtName,"r") as f:
            doc = ass.parse(f)
            for event in doc.events:
                lines.append(cleanLine(event.text))
        combined = "\n".join(lines)
        if "in" in combined or "to" in combined or "for" in combined:
            output += combined
    return output

def extractFromFile(fileName,ep):
    os.makedirs("kearnie/screencaps/",exist_ok=True)
    text = extractTextFromSubtitles(fileName)
    with open("kearnie/subs.txt","a") as f:
        f.write(text)
    takeSnapshots(fileName,ep)
def extractSeries():
    ep = 0
    for filename in os.listdir("."):
        if filename.endswith(".mkv"):
            extractFromFile(filename,ep)
            ep += 1

extractSeries()

Processing code:

import rita.*;
import java.util.*;

//RiTa Markov Chain example template
//Does not contain JSON attributes yet 🙁 

JSONArray subtitles;
int count = 0;

RiMarkov markov;
String line = "click to (re)generate!";
String[] files = { "../data/durarara.txt", 
                   "../data/fmab.txt",
                   "../data/geass.txt",
                   "../data/naruto.txt",
                   "../data/wata.txt" };
int x = 160, y = 240;

void setup()
{
  size(500, 500);

  fill(0);
  textFont(createFont("times", 16));

  // create a markov model w' n=2 from the files
  markov = new RiMarkov(2);
  markov.loadFrom(files, this);
  subtitles = new JSONArray();
}

void draw()
{
  background(250);
  text(line, x, y, 400, 400);
}

void mouseClicked()
{
  if (!markov.ready()) return;
  x = y = 50;
  String[] lines = markov.generateSentences(1);
  line = RiTa.join(lines, " ");
  
  JSONObject subtitle = new JSONObject();
  subtitle.setString("subtitle", lines[0]);
  subtitles.setJSONObject(count, subtitle);
  println(lines[0]);
  if (lines[0] != null) {
    saveJSONArray(subtitles, count + ".json");
  count++;
  }
}

Basil.js code:

#includepath "~/Documents/;%USERPROFILE%Documents";
#includepath "d:\\Documents";
#include "basiljs/bundle/basil.js";

// Load a data file containing your book's content. This is expected
// to be located in the "data" folder adjacent to your .indd and .jsx. 
var jsonString = b.loadString("finalsubs.json");
var jsonData;


//--------------------------------------------------------
function setup() {
  var pageFlip = 0;
  // Clear the document at the very start. 
  b.clear (b.doc());

  // Make a title page. 
  // b.fill(0,0,0);
  // b.textSize(24);
  // b.textFont("Calibri"); 
  // b.textAlign(Justification.LEFT_ALIGN); 
  // b.text("A Basil.js Alphabet Book", 72,72,360,36);
  // b.text("Golan Levin, Fall 2016", 72,108,360,36);

  
  // Parse the JSON file into the jsonData array
  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);


  // Initialize some variables for element placement positions.
  // Remember that the units are "points", 72 points = 1 inch.
  var titleX = 72; 
  var titleY = 72;
  var titleW = 72;
  var titleH = 72;

  var captionX = 38; 
  var captionY = b.height - 115;
  var captionW = 500;
  var captionH = 36;

  var imageX = 38; 
  var imageY = 72; 
  var imageW = 500; 
  var imageH = 288; 


  // Loop over every element of the book content array
  // (Here assumed to be separate pages)
  for (var i = 0; i < jsonData.length; i++) {

    // Create the next page. 
    b.addPage();
    b.println(pageFlip);
    if (pageFlip == 0) {
        b.println("*******");
        // Load an image from the "images" folder inside the data folder;
        // Display the image in a large frame, resize it as necessary. 
        b.noStroke();  // no border around image, please.
        var anImageFilename = "images/" + b.floor(b.random(0,40)) + ".png"
        var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
        anImage.fit(FitOptions.PROPORTIONALLY);
        pageFlip = 1;
        b.println("*")
    }
    
    if (pageFlip == 1) {
    // Create textframes for the "title" field.
    // Draw an ellipse with a random color behind the title letter.
    /*b.noStroke(); 
    b.fill(b.random(180,220),b.random(180,220),b.random(180,220)); 
    b.ellipseMode(b.CORNER);
    b.ellipse (titleX,titleY,titleW,titleH);

    b.fill(255);
    b.textSize(56);
    b.textFont("Calibri"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text(jsonData[i].title, titleX,titleY,titleW,titleH);*/

    // Create textframes for the "caption" fields
    b.stroke(0);
    b.fill(255);
    b.textSize(20);
    b.textFont("Calibri"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.TOP_ALIGN );
    b.text(jsonData[i].caption, captionX,captionY,captionW,captionH);
    
    pageFlip = 0;
    
    }
  };
}

// This makes it all happen:
b.go(); 
Written by Comments Off on Keali-Book Posted in Book

Xastol – Book

rip_00

rip_00_xastol

rip_01_xastol
 

#RIP is a generative book that showcases the use of the hashtag “#rip” on Twitter for October 21st, 2016. Using Temboo to gain easy access to the Twitter AI for collecting data, random tweets with the given hashtag are accessed and placed over 8-bit tombstones. In correlation with the randomly selected tweets are randomly generated backgrounds. The tombstones, on which the tweets are placed, are the same size and shape and flip horizontally every page (even pages vs. odd pages). Additionally, the background colors are randomly generated with dark color range to give off a somber feel that contrasts with with the general sarcastic mood of the tweets.

sketches_book_rip_xastol

The idea for my book actually sprang up while in the studio. After coming up with many over-complicated ideas and trying to figure out how realistic those ideas were while facing the learning curve of basil.js, I was stumped. That’s when a friend (takos) went into detail about us suffering and how we were going to probably spend the entire night in the studio, I responded with “rip” and then got myself thinking about how the term is used in slang. Initially, the term comes from “R.I.P.” or “Rest In Peace”, generally used when expressing ones condolences to the end of another’s life. However, the term has evolved into the singular word “rip”, and is often used to express sympathy with minor incidents of negative connotation.

I.E.

Person 1: “I just stubbed my pinkie toe and it hurts.”

Person 2: “rip.”

To help this come to life, I created two programs: one that would scrape data from Twitter and one that would generate the visuals. After being introduced to the website Temboo, gaining access to tweets on Twitter became trivial, as the website provides code for accessing the Twitter AI and Search features. This was completed in Processing.

I wanted to create visuals that would connect the traditional and modern usage of the term and found that Processing would also be useful for this. To connect the usages, I decided to create an 8-bit tombstone against a dark background (modern visuals against traditional feel).

Lastly, I converted my information into .json files (also done through Processing), so that data would be easily transmitted into InDesign (using basil.js).

In terms of my final product, I am satisfied, but not fully pleased. I am satisfied with the outcome of my project, given my limited knowledge on formatting print media and limited window to work on the project due to other class projects. Mainly, I think it’s the aesthetics of the book that lack, compared to the concept itself. I think if given more time to work with basil.js and print media, I’ll be able to create a much more aesthetically pleasing and content full piece.

 

Book Link: http://www.blurb.com/bookstore/invited/6594146/096a0b83792047730ec84f6dc02ab9d8dcb10012

PDF Link: rip-xapostol

Github Link: https://github.com/xapostol/60-212/tree/master/GenBook-xastol

 

TWITTER DATA COLLECTION CODE

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;

// Create a session using your Temboo account application details
TembooSession session = new TembooSession("xastol", "myFirstApp", "WHATEVER YOURS IS");

void setup() {
  // Run the Tweets Choreo function
  runTweetsChoreo();
}

void runTweetsChoreo() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);

  // Set inputs
  String myQuery  = "rip";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken("YOUR STUFF");
  tweetsChoreo.setConsumerKey("THEIR STUFF");
  tweetsChoreo.setConsumerSecret("SECRET THEIR STUFF");
  tweetsChoreo.setAccessTokenSecret("SECRET YOUR STUFF");

  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();
  
  // Print results
  println(tweetsResults.getLimit());
  println(tweetsResults.getRemaining());
  println(tweetsResults.getReset());
  //println(tweetsResults.getResponse());

  String[] result = {tweetsResults.getResponse()};
  println(result);
  saveStrings(myQuery+".json",result);
}

 

VISUALS CODE

// Template Provided by Golan
// Xavier Apostol (Xastol)
// 60-212 (8:30 - 11:20am & 1:30 - 4:20pm)
// xapostol@andrew.cmu.edu
// Random Character Generation

import processing.pdf.*;
boolean bRecordingPDF;
int pdfOutputCount = 1; 

float count = 1;
float changeFac = 40;
 
void setup() {
  size(750, 750);
  bRecordingPDF = true;
  strokeWeight(1);
}
 
void keyPressed() {
  // When you press a key, it will initiate a PDF export
  bRecordingPDF = true;
  count += 1;
  
  if (count % 2 == 1) {
    changeFac = 40; 
  } else {
    changeFac = -40; 
  }
}
 
void draw() {
  if (bRecordingPDF) {
    float backGCol = 255;
    float ranR = random(50);
    float ranG = random(50);
    float ranB = random(50);
    background(255); // this should come BEFORE beginRecord()
    beginRecord(PDF, "tombstone" + pdfOutputCount + ".pdf");
 
//--------------------------------------------------------------
    //Make all drawings here.
    noFill(); 
    beginShape();
    float tombX = width/2;
    float tombY = height/2;
    float tombW = tombX*1.15;
    float tombH = tombY*1.5;
    
    
    rectMode(CENTER);
    noStroke();
    /*
    // Tombstone
    fill(100,100,100);
    rect(tombX,tombY, tombW,tombH);
    rect(tombX,tombY, tombW-20,tombH+20);
    rect(tombX,tombY, tombW-40,tombH+40);
    rect(tombX,tombY, tombW-60,tombH+60);
    
    fill(150,150,175);
    rect(tombX+changeFac,tombY, tombW,tombH);
    rect(tombX+changeFac,tombY, tombW-20,tombH+20);
    rect(tombX+changeFac,tombY, tombW-40,tombH+40);
    rect(tombX+changeFac,tombY, tombW-60,tombH+60);
    */
    fill(ranR,ranG,ranB);
    rect(width/2,height/2, width,height);
    
    endShape();
//--------------------------------------------------------------
    endRecord();
    bRecordingPDF = false;
    pdfOutputCount++;
  }
}

 

INDESIGN CODE

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
var jsonString = b.loadString("clean_tweets_sensored.json");
var jsonData;
//--------------------------------------------------------
function setup() {
// Clear the document at the very start.
b.clear (b.doc());
// Initialize some variables for element placement positions.
// Remember that the units are "points", 72 points = 1 inch.
var titleX;
var titleY = 72;
var titleW = 1080;
var titleH = 72;
var captionX;
var captionY = b.height/2;
var captionW = b.width - b.width/2;
var captionH = 180;
var imageX = 6;
var imageY = 10;
var imageW = 72*7;
var imageH = 72*7;
var coverFileName = "images/tomb1.png";
var coverImage = b.image(coverFileName, imageX, imageY, imageW, imageH);
// Make a title page.
coverImage.fit(FitOptions.PROPORTIONALLY);
b.fill(0);
b.textSize(200);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN);
b.text("#RIP", 12,90,480,360);
b.textSize(30);
b.text("Xavier Apostol", 153,339,360,72);
b.text("Fall 2016", 153,369,360,72);

// Make a info page.
b.addPage();
b.textSize(36);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN);
b.text("A Generative Book Using Twitter Hashtags", 72,162,360,180);

b.addPage();
b.textSize(36);
b.text("For Friday October 21st, 2016", 72,191,360,121);
// Parse the JSON file into the jsonData array
jsonData = b.JSON.decode( jsonString );
b.println("Number of elements in JSON: " + jsonData.length);
// Loop over every element of the book content array
// (Here assumed to be separate pages)
for (var i = 0; i < jsonData.length; i++) {

// Create the next page.
b.addPage();

// Load an image from the "images" folder inside the data folder;
// Display the image in a large frame, resize it as necessary.
b.noStroke(); // no border around image, please.
var anImageBFilename = "background/" + jsonData[i].image;
var anImageFilename;
if (i % 2 == 0) {
anImageFilename = "images/tomb1.png";
titleX = -b.width/2 - 10;
captionX = b.width/2 - b.width/5;

} else {
anImageFilename = "images/tomb2.png";
titleX = -b.width/2 - 40;
captionX = b.width/2 - b.width/4 - 10;
}

var anImageBack = b.image(anImageBFilename, imageX - 6, imageY-10, imageW+1, imageH);
var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
anImageBack.fit(FitOptions.PROPORTIONALLY);
anImage.fit(FitOptions.PROPORTIONALLY);

// Create textframes for the "screenName" field.
b.fill(0);
b.textSize(30);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
b.text(jsonData[i].screenName, titleX,titleY,titleW,titleH);

// Create textframes for the "date" fields
b.textSize(20);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.TOP_ALIGN );
b.text(jsonData[i].date, captionX,titleY+108,captionW,captionH);

// Create textframes for the "text" fields
b.textSize(20);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.TOP_ALIGN );
b.text(jsonData[i].text, captionX,captionY,captionW,captionH);

};
// For even amount of pages.
b.addPage();
var endPageFileName = "background/tombstone1-page-001.jpg";
var endPage = b.image(endPageFileName, imageX - 6, imageY-10, imageW+1, imageH);
endPage.fit(FitOptions.PROPORTIONALLY);
}

// This makes it all happen:
b.go();

Video of the professor flipping through my book:

Written by Comments Off on Xastol – Book Posted in Book

Ngdon-Book

Book of Illustrations of Imaginary Creatures

snip20161021_28

snip20161021_17

aasdfawelfhd

A PDF iteration of my book

View on Blurb

My book is a book of procedurally generated illustrations of imaginary creatures. Every generated book is different, detailing different creatures from different islands. Each of them is titled “Fauna of <Island Name>”, and has a map of that island and illustrations of 50 creatures living on that island, along with their Latin names.

Inspiration

My main inspiration is the book Classic of Mountains and Seas from 4th century BC China. The book contains detailed description of hundreds of weird animals that probably didn’t exist, such as snake headed bird, and tiger with nine heads, and headless humanoid with nipples as its eyes and belly button as its mouth, etc. This book also has geographic description of mountains and rivers, and together they rendered a fantastic world of prehistoric China.

 

 

 

 

 

 

 

 

Another inspiration is zoological illustrations. They are usually very carefully drawn, detailing every hair and every feather. However, creatures in these illustrations usually have a stiff, deadpan and lifeless look. This contrast gives them a very peculiar aesthetic that I found interesting.

Techniques

Skeleton
snip20161027_2

I started by making a general skeleton for my creatures. The skeleton consists of bones, and each bone object has only the following attributes:

  1. A pointer to its parent bone
  2. An array of pointer(s) to its child bone(s)
  3. Relative rotation to its parent bone
  4. Length of the bone

Thus the absolute position, rotation and other information of a bone can be calculated recursively. This proved to be a very versatile and robust data structure.

Flesh

snip20161027_4

For every bone I defined an additional attribute: The thickness of flesh around it. This way we can get a rough outline of the animal. The thickness is alway perpendicular to the bone and calculated from the front end of the bone.

To make the outline look more organic, I first tried to apply a simple quadratic bezier curve on it. But the outline got smoothed so much that it looked like the animal was made of rubber hoses. Then I discovered the “Rational Bezier Curve”, which assigns a weight on each point. With it I’m able to make the outline just smooth enough.

Rational Bezier Curve (From Wikipedia)

snip20161027_5

Fur

Now that we’ve got the shape of the creature, we can wrap it with its fur. I used the following algorithm to calculate the direction of each hair.

Each hair is enclosed by two curves: One is the outline of the creature, and the other is the skeleton of the creature. There is a point on each of the two curves that is the closest to the hair. Therefore, the direction of the hair is a value between the derivatives of the two curves at these two points. So we have:

Angle of Hair = Tangent of First Curve * Distance to Second Curve/ Distance between First and Second Curve + Tangent of Second Curve * Distance to First Curve/ Distance between First and Second Curve

Then it becomes a simple matter of drawing lots and lots of hair and tweaking the hue and darkness based on the hair’s relative position.

snip20161027_7

Fur Pattern

I Designed three types of patterns: stripes, dots, and rings using a very simple algorithm.

For the stripes, I use mod on the coordinate of each hair to determine which color region it is in. For the dots and rings, I pre-generate a bunch of random points at the beginning of the program, and before drawing each hair, calculate its distance to each point to see if it’s in the radius.

I used Mitchell’s Best Candidate algorithm Golan showed us a few classes ago to generate the random points, so there’s minimal overlapping.

snip20161027_10

snip20161027_9

snip20161027_8

Feathers & Wings

Wings consists of feathers. Here’s my algorithm for a single feather:

snip20161027_11

Notice that the angle of the lines on each side of the shaft changes from one end to the other: It’s lerped from PI/4 to 0. Notice also that the length of the lines also changes: It’s enveloped with an ellipsoid function.

Feathers are then pasted onto the wing just like how fur was drawn onto the skin, with their colors, sizes, directions and shapes calculated based on their relative location to the outline and the skeleton.

snip20161018_6

Misc

Horns – A function that takes a curve and draw a horn shape bent along that curve. This is handy because claws, teeth, tongue can all use this function to draw.

snip20161027_28

Horse tail – A function that takes a curve and draws a bunch of curves that are similar to this curve. It calculates the tangent at each point on the curve, and randomize that angle using Perlin noise to generate the other curves.

Scale – reptilian scales are generated the same way fur is, except they’re orderly arranged regular polygons rather than random short lines.

Randomization

snip20161021_29

I divided the types of creatures my program shall generate into three rough categories: mammals, fishes, birds, then randomized based on special features of these categories.

At first I used the simple random() function to create randomness. The result was chaotic. If the random range was large, I got a jumbled mess of fur and feathers across the screen; If I made the range smaller, I got identical creatures every time.

So I utilized Gaussian Randomness, which has normally distributed output values. I wrote a custom function that took a peak number and a minimum and maximum number and warps gaussian randomness to that range. Therefore for my generator, it is possible, for example, to have extremely long-necked and extremely short-necked animals, however most animals have not-too-long-and-not-too-short necks, which is realistic.

Pseudo-Latin Names

I generated a Latin name for each of my creature with a simple algorithm:

  • Download a list of latin names for real animals from the web.
  • Parse and split all the names into categorized lists of syllables, prefixes and suffixes.
  • Grab a random prefix, grab a couple of random syllables, grab a random suffix.
  • Boom! A pseudo-latin name.

At first I wanted to use a Markov chain word generator, but after playing with it I thought my own method would be more straightforward.

The Island

snip20161027_17

Now that I had my creatures complete, I wanted to make up a place for them to dwell in. First I made a place name generator similar to the latin name generator, then generated island maps using Perlin noises and filters.

At first I also wanted to have a brief description of each creature, along with information about where on the island the creature could be found. These eventually weren’t realized because I found that to write a really good implementation of these features would take days, and I didn’t want a crappy implementation to be next to my carefully generated creatures.

The part of organizing everything and putting them into the book form was rather easy; I used a Basil.js script to automate the process.

Result

I’m actually a bit disappointed with the quality of the printed book. The colors aren’t accurate: The cream background becomes a greenish grey, and everything is darker than it’s supposed to be. Looking at it closely I find that my delicate lines are blurry. The paper and the binding are average. Given that it costs so much, I was expecting something of a much higher quality.

However, nobody else seems to notice, so probably it’s just me nitpicking.

Thoughts

I think this book project can be one in a series of projects or as a part of a larger project. For example, I can generate some weird plants and put them into a book called “Flora of <Island Name>” as a companion to this “Fauna of <Island Name>” book. I can also make a big book about an island, with information about its geography, landscape, animals and plants, climate, history, etc. Or maybe a even bigger book about an imaginary planet, or a bigger bigger book about an imaginary universe.
snip20161027_18

A page from the book

Code

Github Link: https://github.com/LingDong-/60-212/tree/master/book

//Imaginary Creatures Generator

boolean debug = false;
boolean export = false;

void regpoly(float x, float y, float r, float n, float ro){
  beginShape();
  for (int i = 0; i < n; i ++){
    vertex(x+r*cos(ro+i/n*PI*2),y-r*sin(ro+i/n*PI*2));
  }
  endShape(CLOSE);
}

float distsum(float[][] P){ 
  float d = 0;
  for (int i = 0; i < P.length-1; i++){
    d += dist(P[i][0],P[i][1],P[i+1][0],P[i+1][1]);
  }
  return d;
}

float[] midpt(float[] p1,float[] p2){
  return new float[] {(p1[0]+p2[0])/2,(p1[1]+p2[1])/2};
}

float[][] revpl(float[][] pl){
  float[][] nl = new float[pl.length][pl[0].length];
  for (int i = 0; i < pl.length; i++){
    nl[pl.length-1-i]=pl[i];
  }
  return nl;
  
}

float[][] bezmh(float[][] P){
  int cpl = 0;
  float[][] plist = new float[10000][2];
  
  for (int j = 0; j < P.length-2; j++){
    
    float[] p0;float[] p1;float[] p2;
    if (j == 0){
      p0 = P[j];
    }else{
      p0 = midpt(P[j],P[j+1]);
    }
    p1 = P[j+1];
    if (j == P.length-3){
      p2 = P[j+2];
    }else{
      p2 = midpt(P[j+1],P[j+2]);
    }
    float pl = distsum(new float[][]{p0,p1,p2});
    for (int i = 0; i < pl; i+= 1){
       float t = i/pl;
       float w=2;
       plist[cpl][0] = (pow(1-t,2)*p0[0]+2*t*(1-t)*p1[0]*w+t*t*p2[0])/(pow(1-t,2)+2*t*(1-t)*w+t*t);
       plist[cpl][1] = (pow(1-t,2)*p0[1]+2*t*(1-t)*p1[1]*w+t*t*p2[1])/(pow(1-t,2)+2*t*(1-t)*w+t*t);
       cpl++;
    }
  }
  float[][] fplist = new float[cpl][2];
  for (int i = 0; i < cpl; i+= 1){
    fplist[i] = plist[i];
  }
  return fplist;
}



public class Limb{
 Limb[] subs = new Limb[32];
 int subslength = 0;
 Limb par = null;
 float l;
 float tl1;
 float tl2;
 float a;
 float[] alim = new float[2];

 public Limb(float l){
   this.l = l;
 }
 public Limb growlimb(float nl, float na){
   Limb newlimb = new Limb(nl);
   newlimb.par = this;
   newlimb.a = na;
   subs[subslength] = newlimb;
   subslength ++;
   return newlimb;
 }

 public float rot(){
   if (par == null){
     return a;
   }else{
     float la = par.rot();
     float nl = la + a;
     return nl;
   }      
 }
 public float[] loc(){
   float r = rot();
   if (par == null){
     return new float[] {l * cos(r),-l * sin(r)};
   }else{
     float[] ll = par.loc();
     return new float[] {ll[0] + l * cos(r), ll[1] - l*sin(r)};
   }   
 }
  public float[] tlloc(int n){
   float[] lo = this.loc();  
    if (par == null){
   if (n== 1){
     return new float[] {lo[0]+tl1*cos(rot()-PI/2),lo[1]-tl1*sin(rot()-PI/2)};     
   } else if (n== 2){
     return new float[] {lo[0]+tl2*cos(rot()+PI/2),lo[1]-tl2*sin(rot()+PI/2)};     
   }
    }
   lo = par.loc();  
   float ro = (rot()+par.rot()+PI)/2+PI;
   if (n== 1){
     return new float[] {lo[0]+par.tl1*cos(ro),lo[1]-par.tl1*sin(ro)};     
   } else if (n== 2){
     return new float[] {lo[0]+par.tl2*cos(ro+PI),lo[1]-par.tl2*sin(ro+PI)};     
   }
   return new float[2];
   
 }
 
 
 public float[] relcoord(float pr, float di){
   float[] parloc;
   float r = rot();
   if (par == null){
     parloc = new float[]{0,0};
   }else{
     parloc = par.loc();
   }
   return new float[]{parloc[0] + pr * l * cos(r) + di * cos(r+PI/2), parloc[1] - pr * l * sin(r) - di * sin(r+PI/2)};
  
 }
 
 public void bounds(float[] bd){
   if (loc()[0] < bd[0]){bd[0] = loc()[0];} if (loc()[0] > bd[1]){bd[1] = loc()[0];}
   if (loc()[1] < bd[2]){bd[2] = loc()[1];} if (loc()[1] > bd[3]){bd[3] = loc()[1];}
   for (int i = 0; i < subslength; i++){
     if (subs[i] != null){
       
       subs[i].bounds(bd);
     }
   } 
 }
 public void flip(){
   if (par != null){
     a = -a;
   }else{
     a = PI-a;
   }
   for (int i = 0; i < subslength; i++){
     if (subs[i] != null){ 
       subs[i].flip();
     }
   } 
 } 
 
 public void drawSkel(){
   if (par != null){
     line(par.loc()[0],par.loc()[1],loc()[0],loc()[1]);
   }
   for (int i = 0; i < subslength; i++){
     if (subs[i] != null){
       
       subs[i].drawSkel();
     }
   }
 }
  public void drawOline(){
   if (par != null){
     line(par.tlloc(1)[0],par.tlloc(1)[1],tlloc(1)[0],tlloc(1)[1]);
     line(par.tlloc(2)[0],par.tlloc(2)[1],tlloc(2)[0],tlloc(2)[1]);
   }
   for (int i = 0; i < subslength; i++){
     if (subs[i] != null){
       stroke(random(255),random(255),random(255));
       subs[i].drawOline();
     }
   }
 }

}

public class Creature{
 float x;
 float y;
 float[][] dotmap = new float [floor(random(200,800))][2];
 float stpwd = random(20,100);
 float[] stpcol = {random(100),random(30),random(10,40)};
 
 Limb head; Limb jaw_u; Limb jaw_l; Limb neck;
 Limb spine_a; Limb spine_b; Limb spine_c; Limb pelvis;
 Limb tail_a; Limb tail_b; Limb tail_c; Limb tail_d;
 
 Limb shoulder_l; Limb forethigh_l; Limb foreleg_l; Limb forepaw_l;
 Limb midshould_l; Limb midthigh_l; Limb midleg_l; Limb midpaw_l;
 Limb hip_l; Limb hindthigh_l; Limb hindleg_l; Limb hindpaw_l;
 Limb wing_a_l; Limb wing_b_l; Limb wing_c_l;
 
 Limb shoulder_r; Limb forethigh_r; Limb foreleg_r; Limb forepaw_r;
 Limb midshould_r; Limb midthigh_r; Limb midleg_r; Limb midpaw_r; 
 Limb hip_r; Limb hindthigh_r; Limb hindleg_r; Limb hindpaw_r;
 Limb wing_a_r; Limb wing_b_r; Limb wing_c_r;

 Limb[] limbs;


 public Creature(){

   pelvis = new Limb(0);
   spine_c = pelvis.growlimb(0,0);
   spine_b = spine_c.growlimb(0,0);
   spine_a = spine_b.growlimb(0,0);
   neck = spine_a.growlimb(0,0);
   head = neck.growlimb(0,0);
   jaw_u = head.growlimb(0,0);
   jaw_l = head.growlimb(0,0);
   shoulder_l = spine_a.growlimb(0,0);
   shoulder_r = spine_a.growlimb(0,0);
   forethigh_l = shoulder_l.growlimb(0,0);
   forethigh_r = shoulder_r.growlimb(0,0);
   foreleg_l = forethigh_l.growlimb(0,0);
   foreleg_r = forethigh_r.growlimb(0,0);
   forepaw_l = foreleg_l.growlimb(0,0);
   forepaw_r = foreleg_r.growlimb(0,0);
   midshould_l = spine_b.growlimb(0,0);
   midshould_r = spine_b.growlimb(0,0);
   midthigh_l = midshould_l.growlimb(0,0);
   midthigh_r = midshould_r.growlimb(0,0);
   midleg_l = midthigh_l.growlimb(0,0);
   midleg_r = midthigh_r.growlimb(0,0);
   midpaw_l = midleg_l.growlimb(0,0);
   midpaw_r = midleg_r.growlimb(0,0); 
   hip_l = pelvis.growlimb(0,0);
   hip_r = pelvis.growlimb(0,0);
   hindthigh_l = hip_l.growlimb(0,0);
   hindthigh_r = hip_r.growlimb(0,0);
   hindleg_l = hindthigh_l.growlimb(0,0);
   hindleg_r = hindthigh_r.growlimb(0,0);
   hindpaw_l = hindleg_l.growlimb(0,0);
   hindpaw_r = hindleg_r.growlimb(0,0); 
   wing_a_l = spine_a.growlimb(0,0);
   wing_a_r = spine_a.growlimb(0,0);
   wing_b_l = wing_a_l.growlimb(0,0);
   wing_b_r = wing_a_r.growlimb(0,0);
   wing_c_l = wing_b_l.growlimb(0,0);
   wing_c_r = wing_b_r.growlimb(0,0);
   tail_a = pelvis.growlimb(0,0);
   tail_b = tail_a.growlimb(0,0);
   tail_c = tail_b.growlimb(0,0);
   tail_d = tail_c.growlimb(0,0);
   
   limbs = new Limb[] {pelvis,spine_c,spine_b,spine_a,neck,head,jaw_u,jaw_l,
   shoulder_l,shoulder_r,forethigh_l,forethigh_r,foreleg_l,foreleg_r,forepaw_l,forepaw_r,
   midshould_l,midshould_r,midthigh_l,midthigh_r,midleg_l,midleg_r,midpaw_l,midpaw_r,
   hip_l,hip_r,hindthigh_l,hindthigh_r,hindleg_l,hindleg_r,hindpaw_l,hindpaw_r,
   wing_a_l,wing_a_r,wing_b_l,wing_b_r,wing_c_l,wing_c_r,tail_a,tail_b,tail_c};
   
  }
  void makedotmap(float s0, float s1){
    for (int i = 0; i < dotmap.length; i ++){
      float[][] cdd = new float[1000][2];
      for (int j = 0; j < cdd.length; j++){
        cdd[j] = new float[]{random(-width*0.2,width*1.2)-trans[0],random(-height*0.2,height*1.2)-trans[1],random(s0,s1)};
      }
      int maxind = 0;
      float maxdist = 0;
      for (int j = 0; j < cdd.length; j++){
        float shortdist = width*height;
        for (int k = 0; k < i; k++){
           float cd = dist(cdd[j][0],cdd[j][1],dotmap[k][0],dotmap[k][1])-cdd[j][2]-cdd[k][2];
           if (cd < shortdist){ shortdist = cd; } } if (shortdist > maxdist){
          maxdist = shortdist;
          maxind = j;
        }
      }
      dotmap[i] = cdd[maxind];
    } 

 }

 public void feather(float len, float bw, float[] col){

   for (int i = 0; i < len; i+=2){ float ang = PI/4*((len-i)/len);//+random(-0.5*PI/i,0.5*PI/i); float cl = bw*0.03*sqrt(pow(len/2,4)-pow(i-len/2,4)); stroke(col[0],col[1],lerp(min(col[2]+50,100),random(col[2],col[2]+50),i*1.0/len)); line(i,0,i+cl*cos(ang),0+cl*sin(ang)); stroke(col[0],col[1],lerp(min(col[2]+50,100),random(col[2],col[2]+50),i*1.0/len)); line(i,0,i+cl*cos(ang),0-cl*sin(ang)); } stroke(col[0],col[1],min(col[2],100)); line(0,0,len,0); } public void antler(float[] p0, float wd, float len, int depth){ if (depth > 0){
    float[][] cur = bezmh(new float[][]{{p0[0],p0[1]},{p0[0]+len/2,random(len/6,len/3)},{p0[0]+len,p0[1]}});
    hornnoil(cur,wd,new float[]{10,20,100});
    for (int i = 0; i < len/40; i++){
      pushMatrix();
      int ri = floor(random(0,cur.length));
      float[] rc = cur[ri];
      translate(rc[0],rc[1]);
      rotate(random(PI/4));
      float pp = 1.0*(cur.length-ri)/cur.length;
      antler(new float[]{0,0},wd*pp,len*random(pp/2,pp),depth-1);
      popMatrix();
    }
  }
 }
 
 
 public void horn(float[][] cur,float bw){
   
   beginShape();
   for (int i = 0; i < cur.length-1; i++){ float tang = PI/2+atan2(cur[i+1][1]-cur[i][1],cur[i+1][0]-cur[i][0]); float cw =bw * (cur.length-i)/cur.length; vertex(cur[i][0]+cw*cos(tang),cur[i][1]+cw*sin(tang)); } for (int i = cur.length-1; i >0; i--){
     float tang = PI/2+atan2(cur[i-1][1]-cur[i][1],cur[i-1][0]-cur[i][0]);
     float cw = bw * (cur.length-i)/cur.length;
     vertex(cur[i][0]+cw*cos(tang),cur[i][1]+cw*sin(tang));
   }   
   endShape(CLOSE);
 }
 
 public void hornil(float[][] cur, float bw, float[] col){

     noStroke();
    for (float i = bw; i > 0; i--){
      fill(col[0],col[1],col[2]*0.5+col[2]*0.5*(bw-i)/bw);
      horn(cur,i);  
    }  
   
 }
 
 public void hornnoil(float[][] cur, float bw, float[] col){

  fill(col[0],col[1],col[2]/3);
  noStroke();
  horn(cur,bw);
   for (int i = 0; i < cur.length-1; i++){

     float tang = PI/2+atan2(cur[i+1][1]-cur[i][1],cur[i+1][0]-cur[i][0]);

     float cw =(bw * (cur.length-i)/cur.length+1)*(0.4+1.1*noise(i*0.05));
     for (int j = 0; j < cw*10; j++){
       float rw = random(-1,1);
       float rc = col[2]*0.4+random(col[2]*0.3*(1-abs(rw)),col[2]*1.2*(1-abs(rw)));
       fill(col[0],col[1],rc);
       ellipse(cur[i][0]+rw*cw*cos(tang),cur[i][1]+rw*cw*sin(tang),1,1);
     }
   }

   
 }
 
 public void anyfill(String filltype, float[][] cur0, float[][] cur1, float[] coldat, float[] furdat, float[] patdat, int amount){
   if (filltype == "fur"){
     furfill(cur0,cur1,coldat,furdat,patdat,amount);
   }else if (filltype == "scale"){
     scalefill(cur0,cur1,coldat,furdat,patdat,amount);
   }else if (filltype == "feather"){
     featherfill(cur0,cur1,coldat,furdat,patdat,amount);
   }
   if (debug){
     strokeWeight(1);noFill();stroke(255,0,0);
     beginShape(); for (int i = 0; i < cur0.length; i++){
       vertex(cur0[i][0],cur0[i][1]);  
     }endShape(); stroke(255,0,255);
     beginShape(); for (int i = 0; i < cur1.length; i++){
       vertex(cur1[i][0],cur1[i][1]);    
     }endShape();
   }
 }
 
 public void furfill(float[][] cur0, float[][] cur1, float[] coldat, float[] furdat, float[] patdat, int amount){
   int ml = min(cur0.length,cur1.length);
   for (int i = 0; i < ml; i++){ float ca = atan2(cur1[i][1]-cur0[i][1],cur1[i][0]-cur0[i][0]); float cl = dist(cur0[i][0],cur0[i][1],cur1[i][0],cur1[i][1]); float d2e = min(i,ml-i)/ml; cl = cl*(1.0+d2e*(0.4-0.8*noise(0.005*i))); cur1[i][0] = cur0[i][0]+cl*cos(ca); cur1[i][1] = cur0[i][1]+cl*sin(ca); } strokeWeight(1); if (ml > 0){
   for (int i = 0; i < amount; i++){
     int ir = floor(random(0,min(cur0.length,cur1.length)-1));
     float r = random(1);
     float xr = floor(lerp(cur0[ir][0],cur1[ir][0],r));
     float yr = floor(lerp(cur0[ir][1],cur1[ir][1],r));
     float driv1 = atan2(cur1[ir+1][1]-cur1[ir][1],cur1[ir+1][0]-cur1[ir][0]);
     float driv2 = atan2(cur0[ir+1][1]-cur0[ir][1],cur0[ir+1][0]-cur0[ir][0]);
     float p = dist(xr,yr,cur1[ir][0],cur1[ir][1])/dist(cur0[ir][0],cur0[ir][1],cur1[ir+1][0],cur1[ir+1][1]);
     float d = driv1*p+driv2*(1-p);
     float dr = d+random(-PI*0.1,PI*0.1)+(1-p)*furdat[2];
     stroke(coldat[0]+p*coldat[1]+random(coldat[2]),coldat[3]+p*coldat[4]+random(coldat[5]),coldat[6]+p*coldat[7]+random(coldat[8]));
     if (patdat[0] == 1){
       if (floor(xr/stpwd)%2 == 0){
         stroke(stpcol[0],stpcol[1],random(stpcol[2]));
       }
     }else if (patdat[0] == 2){
       for (int j = 0; j < dotmap.length; j++){
         if (dist(xr,yr,dotmap[j][0],dotmap[j][1])<dotmap[j][2]){
           stroke(stpcol[0],stpcol[1],random(stpcol[2]));
         }
       }
     }else if (patdat[0] == 3){
       for (int j = 0; j < dotmap.length; j++){
         if (dist(xr,yr,dotmap[j][0],dotmap[j][1])<dotmap[j][2] && dist(xr,yr,dotmap[j][0],dotmap[j][1])>dotmap[j][2]/2){
           stroke(stpcol[0],stpcol[1],random(stpcol[2]));
         }
       }       
     }
     float fl = furdat[0]+furdat[1]*noise(0.01*i);
     line(xr,yr,xr+fl*cos(dr),yr+fl*sin(dr));  

   }  
   }

 }
 public void furball(float x, float y,float r,float[] coldat){
   for (int i = 0; i < 100; i++){
     stroke(coldat[0]+0*coldat[1]+random(coldat[2]),coldat[3]+0*coldat[4]+random(coldat[5]),coldat[6]+0*coldat[7]+random(coldat[8]));
     line(x+random(-r,r),y+random(-r,r),x+random(-r,r),y+random(-r,r)); 
   }
 }
 
 public void scalefill(float[][] cur0, float[][] cur1, float[] coldat, float[] furdat, float[] patdat, int amount){
   int ml = min(cur0.length,cur1.length);
   for (int i = 0; i < ml; i++){
     float ca = atan2(cur1[i][1]-cur0[i][1],cur1[i][0]-cur0[i][0]);
     float cl = dist(cur0[i][0],cur0[i][1],cur1[i][0],cur1[i][1]);
     float d2e = min(i,ml-i)/ml;
     cl = cl*(1.0+d2e*(0.4-0.8*noise(0.005*i)));
     cur1[i][0] = cur0[i][0]+cl*cos(ca);
     cur1[i][1] = cur0[i][1]+cl*sin(ca);
     
   }   
   strokeWeight(1);
   amount = amount/20;
   for (int i = 0; i < amount; i+=5){
     int ir = floor(random(0,min(cur0.length,cur1.length)-1));
     float cd =  dist(cur0[ir][0],cur0[ir][1],cur1[ir][0],cur1[ir][1]);
     for (int j = 0; j < cd; j+=5){
       float r = random(1);
       float xr = floor(lerp(cur0[ir][0],cur1[ir][0],r));
       float yr = floor(lerp(cur0[ir][1],cur1[ir][1],r));
       float driv1 = atan2(cur1[ir+1][1]-cur1[ir][1],cur1[ir+1][0]-cur1[ir][0]);
       float driv2 = atan2(cur0[ir+1][1]-cur0[ir][1],cur0[ir+1][0]-cur0[ir][0]);
       float p = dist(xr,yr,cur1[ir][0],cur1[ir][1])/dist(cur0[ir][0],cur0[ir][1],cur1[ir+1][0],cur1[ir+1][1]);
       float d = driv1*p+driv2*(1-p);
       float dr = d+random(-PI*0.1,PI*0.1)+(1-p)*furdat[2];
       //dr = random(PI*2);
       float[] col = new float[]{coldat[0]+p*coldat[1]+random(coldat[2]),coldat[3]+p*coldat[4]+random(coldat[5]),coldat[6]+p*coldat[7]+random(coldat[8])};
       fill(col[0],col[1],col[2]);
       stroke(col[0],col[1]*0.8,col[2]*0.6);
       regpoly(xr,yr,4,5,dr);

     }
   }     
   
   for (int i = 0; i < ml-1; i+=6){
     int ir = i;//floor((i*1.0)/amount*(ml-1));//floor(random(0,min(cur0.length,cur1.length)-1));
     float cd =  dist(cur0[ir][0],cur0[ir][1],cur1[ir][0],cur1[ir][1]);
     for (int j = 0; j < cd; j+=6){ float r = j/cd;//random(1); float xr = floor(lerp(cur0[ir][0],cur1[ir][0],r)); float yr = floor(lerp(cur0[ir][1],cur1[ir][1],r)); float driv1 = atan2(cur1[ir+1][1]-cur1[ir][1],cur1[ir+1][0]-cur1[ir][0]); float driv2 = atan2(cur0[ir+1][1]-cur0[ir][1],cur0[ir+1][0]-cur0[ir][0]); float p = dist(xr,yr,cur1[ir][0],cur1[ir][1])/dist(cur0[ir][0],cur0[ir][1],cur1[ir+1][0],cur1[ir+1][1]); float d = driv1*p+driv2*(1-p); float dr = d+random(-PI*0.1,PI*0.1)+(1-p)*furdat[2]; float[] col = new float[]{coldat[0]+p*coldat[1]+random(coldat[2]),coldat[3]+p*coldat[4]+random(coldat[5]),coldat[6]+p*coldat[7]+random(coldat[8])}; fill(col[0],col[1],col[2]); stroke(col[0],col[1]*0.8,col[2]*0.6); regpoly(xr,yr,6,5,dr); } } } public void featherfill(float[][] cur0, float[][] cur1, float[] coldat, float[] furdat, float[] patdat, int amount){ int ml = min(cur0.length,cur1.length); if (ml > 0){
   strokeWeight(1);
   float a0 = atan2(cur0[1][1]-cur0[0][1],cur0[1][0]-cur0[0][0])+PI/2;
   float a1 = atan2(cur0[cur0.length-1][1]-cur0[cur0.length-2][1],cur0[cur0.length-1][0]-cur0[cur0.length-2][0]);

   for (int k = 0; k <=4; k+=1){
     for (float i = 1; i < ml-1; i*=1.05){
       int ir = floor(i);
       float cd =  dist(cur0[ir][0],cur0[ir][1],cur1[ir][0],cur1[ir][1]);
       
       float j = (4-k)*cd/4;
       float r = j/cd;
       float xr = floor(lerp(cur0[ir][0],cur1[ir][0],r));
       float yr = floor(lerp(cur0[ir][1],cur1[ir][1],r));
       pushMatrix();
       translate(xr,yr);
       rotate(lerp(a0,a1,(i*1.0)/ml));
       feather(lerp(20,100,j/cd*(i*1.0)/ml+random(-0.1,0.1)),lerp(0.5,5,(cd-j)/cd*(ml-i)/ml),new float[]{coldat[0],coldat[3],pow(j/cd,0.9)*coldat[6]+random(20)*coldat[7]});
       popMatrix();
       
     }
   }    
 } }

 
 public void featherfin(float[][] cur,float[] coldat){
  for(int i = 0; i < cur.length-1; i++){
    float tng = atan2(cur[i+1][1]-cur[i][1],cur[i+1][0]-cur[i][0]);
     pushMatrix();
     translate(cur[i][0],cur[i][1]);
     rotate(tng+random(-PI*0.1,PI*0.1));
     feather(100,0.1,new float[]{coldat[0],coldat[3],pow(0.5,0.9)*coldat[6]+random(20)});
     popMatrix();
  }
 } 
 
 public void feathertail(float[][] cur, float[] coldat){
  for(float j = 1; j < cur.length-1; j=j*1.5){
    int i = floor(j);
    float tng = atan2(cur[i+1][1]-cur[i][1],cur[i+1][0]-cur[i][0]);
    for (float k = 0; k < 1; k++){
     pushMatrix();
     translate(cur[i][0],cur[i][1]);
     rotate(tng+random(-PI/10*(cur.length-i)/cur.length,PI/10*(cur.length-i)/cur.length));
     feather(lerp(50,200,j/cur.length),lerp(0.5,0.001,j/cur.length),new float[]{coldat[0],coldat[3],pow(0.5,0.9)*coldat[6]+random(20)});
     popMatrix();
    }
  }
 } 
 public void drawfeathertail(float[] coldat){
   feathertail(bezmh(new float[][]{pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()}),coldat);   
 }
 
 
 
 
 
 public void draweye(float pr,float di, float rad,float[] col1, float[] col2, float[] coldat, float[] furdat, float[]patdat){

  float[] p1;
  p1 = head.relcoord(pr,di);//0.75,-25
  stroke(col1[0],col1[1],col1[2]/10);
  strokeWeight(5);
  fill(col1[0],col1[1],col1[2]);
  ellipse(p1[0],p1[1],rad,rad);
  
  noStroke();
  
  for (float i = 0; i < PI*2; i+=PI*0.1){
    fill(col1[0],col1[1],col1[2]*random(0.8,1.0));
    float rr = random(rad*0.4);
    ellipse(p1[0]+rr*cos(i),p1[1]+rr*sin(i),2,2);
  }
  
  stroke(col2[0],col2[1],col2[2]*2);
  strokeWeight(2);
  fill(col2[0],col2[1],col2[2]);
  
  
  ellipse(p1[0],p1[1],rad/3,rad/3);
  strokeWeight(1);
  for (float i = 0; i < PI*2; i+=PI*0.2){
    stroke(col2[0],col2[1],col2[2]*1.5);
    line(p1[0]+rad/10*cos(i),p1[1]+rad/10*sin(i),p1[0]+rad/6*cos(i),p1[1]+rad/6*sin(i));
  }

  furfill(
  bezmh(new float[][]{head.relcoord(pr+0.45,di),head.relcoord(pr+0.05,di-rad+min(rad,13)),head.relcoord(pr-0.25,di+5)}),
  bezmh(new float[][]{head.relcoord(pr+0.45,di),head.relcoord(pr+0.04,di-rad-5),head.relcoord(pr-0.25,di+5)}),
  coldat,new float[]{furdat[0]/3,furdat[1]/3,furdat[2]},patdat,floor(furdat[3]/100));
  furfill(
  bezmh(new float[][]{head.relcoord(pr+0.45,di),head.relcoord(pr+0.25,di+rad-min(22,rad)),head.relcoord(pr-0.05,di+rad-min(10,rad)),head.relcoord(pr-0.25,di+5)}),
  bezmh(new float[][]{head.relcoord(pr+0.45,di),head.relcoord(pr-0.05,di+rad),head.relcoord(pr-0.25,di+5)}),
  coldat,new float[]{furdat[0]/3,furdat[1]/3,furdat[2]},patdat,floor(furdat[3]/100));  
   
 }
 public void drawclaw(Limb leg, Limb paw, float size, float bend, float[] col, float[] coldat, float[] furdat, float[] patdat){

  hornil(bezmh(new float[][]{paw.relcoord(1.0,-2),paw.relcoord((size+1)/2,-bend-4),paw.relcoord(size,-5)}),4,col);
  hornil(bezmh(new float[][]{paw.relcoord(1.0,0),paw.relcoord((size+1)/2,-bend),paw.relcoord(size,0)}),4,col);
  hornil(bezmh(new float[][]{paw.relcoord(1.0,2),paw.relcoord((size+1)/2,-bend+4),paw.relcoord(size,5)}),4,col);

  furfill(
  bezmh(new float[][]{paw.relcoord(0.8,0),paw.loc(),paw.relcoord(0.8,0)}),
  bezmh(new float[][]{paw.relcoord(0.8,0),paw.relcoord(1.0,5),paw.relcoord(1.0,-15),paw.relcoord(0.8,0)}),
  coldat,new float[]{furdat[0]/3,furdat[1]/3,furdat[2]},patdat,floor(furdat[3])); 
   
 }
 public void drawear(float wd, float ht, float[] incol, float[] coldat,float[] furdat, float[] patdat){
  float b = -neck.tl1/2;
  furfill(
  bezmh(new float[][]{neck.loc(),neck.relcoord(1.0,b-ht),neck.loc()}),
  bezmh(new float[][]{neck.relcoord(1+wd,0),neck.relcoord(1.0,b-ht-20),neck.relcoord(1-wd,0)}),
  coldat,furdat,patdat,floor(furdat[3]/100)); 
  furfill(
  bezmh(new float[][]{neck.relcoord(1,b-ht/6),neck.relcoord(1.0,b-ht/2),neck.relcoord(1,b-ht/6)}),
  bezmh(new float[][]{neck.relcoord(1+wd/3,b-ht/4),neck.relcoord(1.0,b-ht),neck.relcoord(1-wd/3,b-ht/4)}),
  incol,furdat,new float[]{0},floor(furdat[3]/100)) ;
 }
   
 public void drawteeth(float wd, float ht, float[] col){
    for (float i = jaw_u.l*0.2; i < jaw_u.l*0.8; i+=wd){

     hornil(bezmh(new float[][]{jaw_u.relcoord(i/jaw_u.l,0),jaw_u.relcoord(i/jaw_u.l,ht),jaw_u.relcoord(i/jaw_u.l,ht+ht*4*noise(i*0.1))}),
     wd,col);
       }
    for (float i = jaw_l.l*0.2; i < jaw_l.l*0.8; i+=wd){


     hornil(bezmh(new float[][]{jaw_l.relcoord(i/jaw_l.l,0),jaw_l.relcoord(i/jaw_l.l,-ht),jaw_l.relcoord(i/jaw_l.l,-ht-ht*10*noise(i*0.1))}),
     wd,col);
     }  
   
 }
 public void drawtusk(float wd, float ht, float un, float ln, float[] col){
    for (float ii = 0; ii < un; ii+=1){
     float i = random(jaw_u.l*0.5,jaw_u.l*0.9);
     hornnoil(bezmh(new float[][]{jaw_u.relcoord(i/jaw_u.l,0),jaw_u.relcoord(i/jaw_u.l,ht),jaw_u.relcoord(i/jaw_u.l,ht+ht*4*noise(i*0.1))}),
     wd,col);
       }
  for (float ii = 0; ii < ln; ii+=1){

     float i = random(jaw_l.l*0.5,jaw_l.l*0.9);
     hornnoil(bezmh(new float[][]{jaw_l.relcoord(i/jaw_l.l,0),jaw_l.relcoord(i/jaw_l.l,-ht),jaw_l.relcoord(i/jaw_l.l,-ht-ht*10*noise(i*0.1))}),
     wd,col);
     } 

 }

 public void drawhorsetail(float[] coldat){

  float[][] cur = bezmh(new float[][]{pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()});
  for (int i = 0; i < 500; i++){
    noFill();
    stroke(coldat[0],coldat[3],random(coldat[6]*3));
    
    beginShape();
    float[] lastp = new float[]{cur[0][0],cur[0][1]};
    float rl = random(1);
    for (int j = 1; j < cur.length*rl; j++){

      float dis = dist(cur[j][0],cur[j][1],cur[j-1][0],cur[j-1][1]);
      float ang = atan2(cur[j][1]-cur[j-1][1],cur[j][0]-cur[j-1][0]);
      float nz = noise(i,j*0.01)*2-1;
      lastp = new float[]{lastp[0]+dis*cos(ang+nz*PI/6),lastp[1]+dis*sin(ang+nz*PI/6)};
      vertex(lastp[0],lastp[1]);
    }
    endShape();
  }
 }
   
 public void fishtail(float[][]cur0, float[][]cur1,float[] coldat){
  float sp = cur1.length*1.0/cur0.length;
  
   for (int i = 1; i < cur0.length; i++){
    
    for (int j = 0; j <= sp; j++){
      stroke(coldat[0],coldat[1],coldat[2]/2);
      strokeWeight(3);
      int ji = floor(i*sp+j);
      if (ji < cur1.length && ji >= 0){
        line(cur0[i][0],cur0[i][1],cur1[ji][0]+random(-5,5),cur1[ji][1]+random(-5,5));

      }
    } 
  
   }
  for (int i = 0; i < cur0.length; i++){
    
    for (int j = 0; j <= sp; j++){
      stroke(coldat[0],coldat[1],random(coldat[2]/3,coldat[2]));
      strokeWeight(1);
      int ji = floor(i*sp+j);
      if (ji < cur1.length && ji >= 0){

        float[] nc = new float[]{cur1[ji][0]+random(-5,5),cur1[ji][1]+random(-5,5)};
        float di = dist(cur0[i][0],cur0[i][1],nc[0],nc[1]);
        
        pushMatrix();
        translate(cur0[i][0],cur0[i][1]);
        rotate(atan2(nc[1]-cur0[i][1],nc[0]-cur0[i][0]));
        float lp = random(-1,1);
        for (int k = 0; k <= di; k+=5){ float lp2 = random(-1,1); line(k,lp,k+5,lp+lp2); lp = lp + lp2; } popMatrix(); } } } } public void drawfeatherfin(float[] coldat){ featherfin(bezmh(new float[][]{pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()}),coldat); } public void drawfin_l(float[] coldat){ featherfin(bezmh(new float[][]{spine_a.loc(),forethigh_l.loc(),forethigh_l.loc()}),coldat); } public void drawfin_r(float[] coldat){ featherfin(bezmh(new float[][]{spine_a.loc(),forethigh_r.loc(),forethigh_r.loc()}),coldat); } public void drawmidfin_l(float[] coldat){ featherfin(bezmh(new float[][]{spine_b.loc(),midthigh_l.loc(),midthigh_l.loc()}),coldat); } public void drawmidfin_r(float[] coldat){ featherfin(bezmh(new float[][]{spine_b.loc(),midthigh_r.loc(),midthigh_r.loc()}),coldat); } public void drawwing(Limb wing_a, Limb wing_b, Limb wing_c, float[] coldat){ scalefill( bezmh(new float[][]{wing_a.relcoord(0,0),wing_a.loc(),wing_b.loc(),wing_c.relcoord(0.7,0)}), bezmh(new float[][]{wing_a.relcoord(0,-40),wing_b.tlloc(1),wing_c.tlloc(1),wing_c.relcoord(0.7,0)}), new float[]{10,5,0,10,10,0,30,10,10},new float[]{6,8,0},new float[]{2},10); featherfill( bezmh(new float[][]{wing_a.relcoord(0,0),wing_a.loc(),wing_b.loc(),wing_c.loc()}), bezmh(new float[][]{wing_a.relcoord(0,-40),wing_b.tlloc(1),wing_c.tlloc(1),wing_c.loc()}), coldat,new float[]{6,8,0},new float[]{2},10); } public void drawtongue(int len){ if (len > 0){
  float[][] cur = new float[len][2];
  for (int i = 0; i < len; i++){ cur[i] = head.relcoord(1+i*0.2,random(10)*i/len); } hornil(bezmh(cur),10,new float[]{0,100,50}); } } public void drawhorn(float wd, float ht, float[]col){ hornnoil(bezmh(new float[][]{head.relcoord(1.0,0),head.relcoord(1.0,-ht/2),head.relcoord(0.5,-ht)}), wd,col); } public void drawbeak(float[] col){ c.hornil(bezmh(new float[][]{c.head.loc(),c.jaw_u.relcoord(0.5,-random(30)),c.jaw_u.loc()}),c.head.tl1/3,col); c.hornil(bezmh(new float[][]{c.head.loc(),c.jaw_l.relcoord(0.5,-random(30)),c.jaw_l.loc()}),c.head.tl2/3,col); } public void drawback(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{neck.loc(),spine_a.loc(),spine_b.loc(),spine_c.loc(),pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()}), bezmh(new float[][]{head.tlloc(1),neck.tlloc(1),spine_a.tlloc(1),spine_b.tlloc(1),pelvis.tlloc(2),tail_b.tlloc(2),tail_c.tlloc(2),tail_c.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawbacknotail(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{neck.loc(),spine_a.loc(),spine_b.loc(),spine_c.loc(),pelvis.loc(),tail_a.loc()}), bezmh(new float[][]{head.tlloc(1),neck.tlloc(1),spine_a.tlloc(1),spine_b.tlloc(1),pelvis.tlloc(2),tail_a.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawbelly(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{neck.loc(),spine_b.loc(),spine_c.loc(),pelvis.loc(),tail_a.loc()}), bezmh(new float[][]{neck.loc(),forethigh_l.relcoord(0.5,0),spine_a.tlloc(2),spine_b.tlloc(2),midpt(hip_l.loc(),hindthigh_l.loc())}), coldat,new float[]{furdat[0]*2,furdat[1],furdat[2]+PI/3},patdat,floor(furdat[3])); } public void drawforeleg_l(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{forepaw_l.loc(),foreleg_l.loc(),forethigh_l.loc(),shoulder_l.loc(),spine_a.loc(),neck.loc()}), bezmh(new float[][]{forepaw_l.loc(),forepaw_l.tlloc(1),foreleg_l.tlloc(1),forethigh_l.tlloc(1),head.tlloc(2)}), coldat,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{forepaw_l.loc(),foreleg_l.loc(),forethigh_l.loc(),shoulder_l.loc(),neck.loc(),spine_a.loc()}), bezmh(new float[][]{forepaw_l.loc(),forepaw_l.tlloc(2),foreleg_l.tlloc(2),forethigh_l.tlloc(2),spine_a.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawforeleg_r(String filltype, float[] coldat, float[] furdat, float[] patdat){ float[] nc = new float[coldat.length]; arrayCopy(coldat,nc); nc[6] = nc[6]/3; nc[7] = nc[7]/3; nc[8] = nc[8]/3; anyfill(filltype, bezmh(new float[][]{forepaw_r.loc(),foreleg_r.loc(),forethigh_r.loc(),shoulder_r.loc(),spine_a.loc(),neck.loc()}), bezmh(new float[][]{forepaw_r.loc(),forepaw_r.tlloc(1),foreleg_r.tlloc(1),forethigh_r.tlloc(1),head.tlloc(2)}), nc,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{forepaw_r.loc(),foreleg_r.loc(),forethigh_r.loc(),shoulder_r.loc(),neck.loc(),spine_b.loc()}), bezmh(new float[][]{forepaw_r.loc(),forepaw_r.tlloc(2),foreleg_r.tlloc(2),forethigh_r.tlloc(2),spine_b.loc()}), nc,furdat,patdat,floor(furdat[3])); } public void drawmidleg_r(String filltype, float[] coldat, float[] furdat, float[] patdat){ float[] nc = new float[coldat.length]; arrayCopy(coldat,nc); nc[6] = nc[6]/3; nc[7] = nc[7]/3; nc[8] = nc[8]/3; anyfill(filltype, bezmh(new float[][]{midpaw_r.loc(),midleg_r.loc(),midthigh_r.loc(),midshould_r.loc(),spine_b.loc()}), bezmh(new float[][]{midpaw_r.loc(),midpaw_r.tlloc(1),midleg_r.tlloc(1),midthigh_r.tlloc(1),spine_b.loc()}), nc,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{midpaw_r.loc(),midleg_r.loc(),midthigh_r.loc(),midshould_r.loc(),spine_c.loc()}), bezmh(new float[][]{midpaw_r.loc(),midpaw_r.tlloc(2),midleg_r.tlloc(2),midthigh_r.tlloc(2),spine_c.loc()}), nc,furdat,patdat,floor(furdat[3])); } public void drawmidleg_l(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{midpaw_l.loc(),midleg_l.loc(),midthigh_l.loc(),midshould_l.loc(),spine_b.loc()}), bezmh(new float[][]{midpaw_l.loc(),midpaw_l.tlloc(1),midleg_l.tlloc(1),midthigh_l.tlloc(1),spine_b.loc()}), coldat,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{midpaw_l.loc(),midleg_l.loc(),midthigh_l.loc(),midshould_l.loc(),spine_c.loc()}), bezmh(new float[][]{midpaw_l.loc(),midpaw_l.tlloc(2),midleg_l.tlloc(2),midthigh_l.tlloc(2),spine_c.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawhindleg_l(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{hindpaw_l.loc(),hindleg_l.loc(),hindthigh_l.loc(),hip_l.loc(),pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()}), bezmh(new float[][]{hindpaw_l.loc(),hindpaw_l.tlloc(2),hindleg_l.tlloc(2),hindthigh_l.tlloc(2),pelvis.loc(),tail_b.tlloc(1),tail_c.tlloc(1),tail_c.loc()}), coldat,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{pelvis.loc(),hip_l.loc(),hindthigh_l.loc(),hindleg_l.loc(),hindpaw_l.loc()}), bezmh(new float[][]{spine_c.tlloc(2),hindthigh_l.tlloc(1),hindleg_l.tlloc(1),hindpaw_l.tlloc(1),hindpaw_l.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawhindlegnotail_l(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{hindpaw_l.loc(),hindleg_l.loc(),hindthigh_l.loc(),hip_l.loc(),pelvis.loc()}), bezmh(new float[][]{hindpaw_l.loc(),hindpaw_l.tlloc(2),hindleg_l.tlloc(2),hindthigh_l.tlloc(2),pelvis.loc()}), coldat,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{pelvis.loc(),hip_l.loc(),hindthigh_l.loc(),hindleg_l.loc(),hindpaw_l.loc()}), bezmh(new float[][]{spine_c.tlloc(2),hindthigh_l.tlloc(1),hindleg_l.tlloc(1),hindpaw_l.tlloc(1),hindpaw_l.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawhindleg_r(String filltype, float[] coldat, float[] furdat, float[] patdat){ float[] nc = new float[coldat.length]; arrayCopy(coldat,nc); nc[6] = nc[6]/3; nc[7] = nc[7]/3; nc[8] = nc[8]/3; anyfill(filltype, bezmh(new float[][]{hindpaw_r.loc(),hindleg_r.loc(),hindthigh_r.loc(),hip_r.loc(),pelvis.loc()}), bezmh(new float[][]{hindpaw_r.loc(),hindpaw_r.tlloc(2),hindleg_r.tlloc(2),hindthigh_r.tlloc(2),pelvis.loc()}), nc,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{pelvis.loc(),hip_r.loc(),hindthigh_r.loc(),hindleg_r.loc(),hindpaw_r.loc()}), bezmh(new float[][]{spine_c.tlloc(2),hindthigh_r.tlloc(1),hindleg_r.tlloc(1),hindpaw_r.tlloc(1),hindpaw_r.loc()}), nc,furdat,patdat,floor(furdat[3])); } public void drawhead(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{spine_a.loc(),neck.loc(),head.loc(),jaw_l.loc(),head.loc(),jaw_u.loc(),head.loc(),neck.loc(),spine_a.loc()}), bezmh(new float[][]{forethigh_l.tlloc(2),head.tlloc(2),jaw_l.tlloc(2),jaw_l.loc(),head.loc(),jaw_u.loc(),jaw_u.tlloc(1),head.tlloc(1),neck.tlloc(1)}), coldat,furdat,patdat,floor(furdat[3])); } public void drawbirdhead(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{spine_a.loc(),neck.loc(),head.loc(),jaw_l.relcoord(0.5,0),head.loc(),jaw_u.relcoord(0.5,0),head.loc(),neck.loc(),spine_a.loc()}), bezmh(new float[][]{forethigh_l.tlloc(2),head.tlloc(2),jaw_l.tlloc(2),jaw_l.relcoord(0.5,0),head.loc(),jaw_u.relcoord(0.5,0),jaw_u.tlloc(1),head.tlloc(1),neck.tlloc(1)}), coldat,furdat,patdat,floor(furdat[3])); } public void drawantler(float[] coldat){ pushMatrix(); translate(neck.loc()[0]-10,neck.loc()[1]-30); rotate(-PI/4); antler(new float[] {0,0},5,200,3); popMatrix(); furball(neck.loc()[0]-10,neck.loc()[1]-30,10,coldat); } } float choice(float a,float b){ if (random(1) > 0.5){
    return a;
  }
  return b;
  
}

float rg(float x, float xmin, float xmax){
  if (random(xmin,xmax) < x){
    return x-abs(randomGaussian()*(x-xmin)/2);
  }
  return x+abs(randomGaussian()*(xmax-x)/2);
}

float rtg(float x, float xmin, float xmax){
  if (random(xmin,xmax) < x){
    return x-abs(randomGaussian()*(x-xmin)/5);
  }
  return x+abs(randomGaussian()*(xmax-x)/5);
}

float[] dark(float[] col){
  return new float[] {col[0],col[1],col[2],col[3],col[4],col[5],col[6]/2.2,col[7]/2.2,col[8]/2.2};
  
}

public int maind(float[] arr){
  int ind = 0;
  float ma = 0;
  for (int i = 0; i < arr.length; i++){ if (arr[i]>ma){
      ind = i;
      ma = arr[i];
    }    
  }
  return ind;
}

public class Generator{
  Creature c;
  float[] col;
  float[] fur;
  float[] pat;
  
  float[] bonecol;
  float[] feathercol;
  float[] fincol;
  float[] scalecol;
  String filltype;
  
  float sixleg = floor(random(4));
  float[] ctype;
  
  float patstyle = floor(random(4));
  float tailstyle = floor(random(3));
  float hoofy = (random(1));
  float clawsize = rg(2,1,2.5);
  float clawbend = rg(5,0,20);
  float eyehue = random(70);
  float winged = random(1);
    
  public Generator(){
    float a = random(7);
    float b = random(4);
    float c = random(4);
    ctype = new float[]{a/(a+b+c),b/(a+b+c),c/(a+b+c)};    
  }

  public void gen(){
    c = new Creature();
    if (patstyle == 0){
      c.makedotmap(10,100);
    }else{
      c.makedotmap(20,20);
    }
    if (random(1) > 0.2){
      filltype = "fur";
    }else{
      filltype = "scale";
    }    
    c.pelvis.tl1 = rg(50,30,100);
    c.pelvis.tl2 = rg(50,30,100);
    
    c.spine_c.l = rg(100,10,200);
    c.spine_c.a = rtg(0.9*PI,0.6*PI,1.2*PI);
    c.spine_c.tl1 = rg(20,5,50);
    c.spine_c.tl2 = rg(100,10,200);
    
    c.spine_b.l = rg(100,10,300);
    c.spine_b.a = rtg(0.1*PI,-0.5*PI,0.5*PI);
    c.spine_b.tl1 = rg(50,10,100);
    c.spine_b.tl2 = rg(100,10,200);
        
    c.spine_a.l = rg(100,10,300);
    c.spine_a.a = rtg(0.1*PI,-0.5*PI,0.5*PI);
    c.spine_a.tl1 = rg(50,10,100);
    c.spine_a.tl2 = rg(50,10,100);
    
    
    c.neck.l = rg(50,0,200);
    c.neck.a = rtg(-0.3*PI,-0.5*PI,-0.0*PI);
    c.neck.tl1 = rg(40,0,60);
    c.neck.tl2 = rg(40,0,60);
    
    
    c.head.l = rg(80,0,200);
    c.head.a = rtg(0.1*PI,-0.5*PI,0.5*PI);
    c.head.tl1 = rg(60,10,100);
    c.head.tl2 = rg(30,10,100);    
    
    
    c.jaw_u.l = rg(80,10,250);
    c.jaw_u.a = rtg(-0.1*PI,-0.4*PI,0.0*PI);
    c.jaw_l.l = rg(80,10,250);
    c.jaw_l.a = rtg(0.1*PI,0.0*PI,0.4*PI);
   

    c.shoulder_l.l = rg(20,10,100);
    c.shoulder_r.l = c.shoulder_l.l;
    c.shoulder_l.a = rtg(0.2*PI,-0.1*PI,0.6*PI);
    c.shoulder_r.a = c.shoulder_l.a + rtg(0,-PI*0.5,PI*0.5);
    c.shoulder_l.tl1 = rg(10,0,80);
    c.shoulder_l.tl2 = rg(50,10,150); 
    c.shoulder_r.tl1 = c.shoulder_l.tl1;
    c.shoulder_r.tl2 = c.shoulder_l.tl2;
    
    c.forethigh_l.l = rg(100,20,200);
    c.forethigh_r.l = c.forethigh_l.l;
    c.forethigh_l.a = rtg(0.4*PI,0.2*PI,0.6*PI);
    c.forethigh_r.a = c.forethigh_l.a + rtg(0,-PI*0.5,PI*0.5);    
    c.forethigh_l.tl1 = c.shoulder_l.tl1 * rg(0.8,0,1);
    c.forethigh_l.tl2 = c.shoulder_l.tl2 * rg(0.8,0,1);; 
    c.forethigh_r.tl1 = c.forethigh_l.tl1;
    c.forethigh_r.tl2 = c.forethigh_l.tl2;    
    
    c.foreleg_l.l = c.forethigh_l.l*rtg(1,0,2);
    c.foreleg_r.l = c.foreleg_l.l;
    c.foreleg_l.a = rtg(-0.5*PI,-0.7*PI,-0.3*PI);
    c.foreleg_r.a = c.foreleg_l.a +  rtg(0,-PI*0.5,PI*0.5);       
    c.foreleg_l.tl1 = c.forethigh_l.tl1  * rg(0.8,0,1);
    c.foreleg_l.tl2 = c.forethigh_l.tl2  * rg(0.8,0,1); 
    c.foreleg_r.tl1 = c.foreleg_l.tl1;
    c.foreleg_r.tl2 = c.foreleg_l.tl2;   
  
 
    if (hoofy < 0.3){ 
      c.forepaw_l.l = c.foreleg_l.l*rtg(0.5,0,1);
      c.forepaw_r.l = c.forepaw_l.l;
      c.forepaw_l.a = rtg(-0.2*PI,-0.7*PI,0.3*PI);
      c.forepaw_r.a = c.forepaw_l.a +  rtg(0,-PI*0.5,PI*0.5);         
    }else{
      c.forepaw_l.l = c.foreleg_l.l*rtg(1,0,2);
      c.forepaw_r.l = c.forepaw_l.l;
      c.forepaw_l.a = rtg(0.3*PI,0.0*PI,0.8*PI);
      c.forepaw_r.a = c.forepaw_l.a +  rtg(0,-PI*0.5,PI*0.5);        
    }
    if (sixleg == 1){
      c.midshould_l.l = c.shoulder_l.l * rtg(1,0.8,1.1);
      c.midshould_r.l = c.midshould_l.l;
      c.midshould_l.a = rtg(0.5*PI,-0.1*PI,0.6*PI);
      c.midshould_r.a = c.midshould_l.a + rtg(0,-PI*0.5,PI*0.5);
      c.midshould_l.tl1 = c.shoulder_l.tl1 * rtg(1,0.8,1.1);
      c.midshould_l.tl2 = c.shoulder_l.tl2 * rtg(1,0.8,1.1); 
      c.midshould_r.tl1 = c.midshould_l.tl1;
      c.midshould_r.tl2 = c.midshould_l.tl2;
      
      c.midthigh_l.l = c.forethigh_l.l * rtg(1,0.8,1.1);
      c.midthigh_r.l = c.midthigh_l.l;
      c.midthigh_l.a = rtg(0.4*PI,0.2*PI,0.6*PI);
      c.midthigh_r.a = c.midthigh_l.a + rtg(0,-PI*0.5,PI*0.5);    
      c.midthigh_l.tl1 = c.midshould_l.tl1 * rg(0.8,0,1);
      c.midthigh_l.tl2 = c.midshould_l.tl2 * rg(0.8,0,1);; 
      c.midthigh_r.tl1 = c.midthigh_l.tl1;
      c.midthigh_r.tl2 = c.midthigh_l.tl2;    
      
      c.midleg_l.l =  c.foreleg_l.l * rtg(1,0.8,1.1);
      c.midleg_r.l = c.midleg_l.l;
      c.midleg_l.a = rtg(-0.5*PI,-0.7*PI,-0.3*PI);
      c.midleg_r.a = c.midleg_l.a +  rtg(0,-PI*0.5,PI*0.5);       
      c.midleg_l.tl1 = c.midthigh_l.tl1  * rg(0.8,0,1);
      c.midleg_l.tl2 = c.midthigh_l.tl2  * rg(0.8,0,1); 
      c.midleg_r.tl1 = c.midleg_l.tl1;
      c.midleg_r.tl2 = c.midleg_l.tl2;   
           
      if (hoofy < 0.3){ c.midpaw_l.l = c.midleg_l.l*rtg(0.5,0,1); c.midpaw_r.l = c.midpaw_l.l; c.midpaw_l.a = rtg(-0.2*PI,-0.7*PI,0.3*PI); c.midpaw_r.a = c.midpaw_l.a + rtg(0,-PI*0.5,PI*0.5); }else{ c.midpaw_l.l = c.midleg_l.l*rtg(1,0,2); c.midpaw_r.l = c.midpaw_l.l; c.midpaw_l.a = rtg(0.3*PI,0.0*PI,0.8*PI); c.midpaw_r.a = c.midpaw_l.a + rtg(0,-PI*0.5,PI*0.5); } } c.hip_l.l = rg(20,10,100); c.hip_r.l = c.hip_l.l; c.hip_l.a = rtg(-0.3*PI,-0.5*PI,0.1*PI); c.hip_r.a = c.hip_l.a + rtg(0,-PI*0.4,PI*0.4); c.hip_l.tl1 = rg(80,10,200); c.hip_l.tl2 = rg(20,0,30); c.hip_r.tl1 = c.hip_l.tl1; c.hip_r.tl2 = c.hip_l.tl2; c.hindthigh_l.l = rg(100,20,200); c.hindthigh_r.l = c.hindthigh_l.l; c.hindthigh_l.a = rtg(-0.5*PI,-0.7*PI,-0.3*PI); c.hindthigh_r.a = c.hindthigh_l.a + rtg(0,-PI*0.4,PI*0.4); c.hindthigh_l.tl1 = rg(20,0,30); c.hindthigh_l.tl2 = rg(20,0,30); c.hindthigh_r.tl1 = c.hindthigh_l.tl1; c.hindthigh_r.tl2 = c.hindthigh_l.tl2; c.hindleg_l.l = rg(100,20,200); c.hindleg_r.l = c.hindleg_l.l; c.hindleg_l.a = rtg(0.5*PI,0.3*PI,0.7*PI); c.hindleg_r.a = c.hindleg_l.a + rtg(0,-PI*0.4,PI*0.4); c.hindleg_l.tl1 = rg(5,0,30); c.hindleg_l.tl2 = rg(20,0,30); c.hindleg_r.tl1 = c.hindleg_l.tl1; c.hindleg_r.tl2 = c.hindleg_l.tl2; c.hindpaw_l.l = rg(80,20,150); c.hindpaw_r.l = c.hindpaw_l.l; c.hindpaw_l.a = rtg(-0.3*PI,-0.6*PI,-0.0*PI); c.hindpaw_r.a = c.hindpaw_l.a + rtg(0,-PI*0.4,PI*0.4); c.wing_a_l.l = rg(100,10,200); c.wing_a_r.l = c.wing_a_l.l; c.wing_a_l.a = rtg(-0.8*PI,-1.0*PI,-0.5*PI); c.wing_a_r.a = c.wing_a_l.a + rtg(0,-PI*0.4,PI*0.4); c.wing_a_l.tl1 = rg(50,0,100); c.wing_a_l.tl2 = rg(0,0,10); c.wing_a_r.tl1 = c.wing_a_l.tl1; c.wing_a_r.tl2 = c.wing_a_l.tl2; c.wing_b_l.l = rg(100,10,200); c.wing_b_r.l = c.wing_b_l.l; c.wing_b_l.a = rtg(0.2*PI,0.0*PI,0.7*PI); c.wing_b_r.a = c.wing_b_l.a + rtg(0,-PI*0.4,PI*0.4); c.wing_b_l.tl1 = rg(80,0,160); c.wing_b_l.tl2 = rg(0,0,10); c.wing_b_r.tl1 = c.wing_b_l.tl1; c.wing_b_r.tl2 = c.wing_b_l.tl2; c.wing_c_l.l = rg(100,10,200); c.wing_c_r.l = c.wing_c_l.l; c.wing_c_l.a = rtg(-0.3*PI,-0.6*PI,0.0*PI); c.wing_c_r.a = c.wing_c_l.a + rtg(0,-PI*0.4,PI*0.4); c.wing_c_l.tl1 = rg(80,0,160); c.wing_c_l.tl2 = rg(0,0,10); c.wing_c_r.tl1 = c.wing_c_l.tl1; c.wing_c_r.tl2 = c.wing_c_l.tl2; c.tail_a.l = rg(100,0,200); c.tail_a.a = rg(0,-PI/3,PI/3); c.tail_a.tl1 = rg(5,0,50); c.tail_a.tl2 = rg(10,0,50); c.tail_b.l = rg(100,0,200); c.tail_b.a = rg(0,-PI/3,PI/3); c.tail_b.tl1 = rg(5,0,50); c.tail_b.tl2 = rg(10,0,50); c.tail_c.l = rg(100,0,200); c.tail_c.a = rg(0,-PI/3,PI/3); c.pelvis.a = -PI+ atan2(c.spine_a.loc()[1]-c.pelvis.loc()[1],c.spine_a.loc()[0]-c.pelvis.loc()[0]); bonecol = new float[]{rtg(5,0,50),random(5,15),90}; fincol = new float[]{random(100),0,0,rg(5,0,50),0,0,random(30),random(0.2,1),0}; feathercol = new float[]{rtg(5,0,80),0,0,rg(5,0,50),0,0,random(60),random(0.5,1),0}; if (filltype == "fur"){ col = new float[]{rtg(5,0,30),2,10,random(10,50),-10,10,random(30),20,50}; }else if (filltype == "scale"){ col = new float[]{random(100),2,10,random(10,50),-10,10,random(20),10,10}; } fur = new float[]{rg(6,0,20),8,0,50000}; pat = new float[]{floor(random(4))}; } public void render(){ //c.drawbody(); if (maind(ctype) == 2){ winged = 1; } switch (maind(ctype)){ case 0: c.drawforeleg_r(filltype,col,fur,pat); c.drawhindleg_r(filltype,col,fur,pat); c.drawclaw(c.foreleg_r,c.forepaw_r,(clawsize+1)/2,clawbend,bonecol,dark(col),fur,pat); c.drawclaw(c.hindleg_r,c.hindpaw_r,(clawsize+1)/2,clawbend,bonecol,dark(col),fur,pat); if (sixleg == 1){ c.drawmidleg_r(filltype,col,fur,pat); c.drawclaw(c.midleg_r,c.midpaw_r,(clawsize+1)/2,clawbend,bonecol,dark(col),fur,pat); } break;case 1: c.drawfin_r(dark(fincol)); if (sixleg == 1){ c.drawmidfin_r(dark(fincol)); } break;case 2: c.drawhindleg_r("scale",col,fur,pat); c.drawhindlegnotail_l("scale",col,fur,pat); c.drawclaw(c.hindleg_r,c.hindpaw_r,clawsize,clawbend,bonecol,dark(col),fur,pat); c.drawclaw(c.hindleg_l,c.hindpaw_l,clawsize,clawbend,bonecol,col,fur,pat); break; } if (winged > 0.8){
      c.drawwing(c.wing_a_r,c.wing_b_r,c.wing_c_r,dark(feathercol));
    }

    switch (maind(ctype)){
      case 0:
        if (tailstyle == 1){
          c.drawhorsetail(dark(col));
          c.drawbacknotail(filltype,col,fur,pat);
          
        }else if (tailstyle == 2){
          c.drawfeatherfin(feathercol);
          c.drawbacknotail(filltype,col,fur,pat);
          
        }else{
          c.drawback(filltype,col, fur, pat);
          
        }break;
      case 1:
        c.drawfeatherfin(fincol);
        c.drawbacknotail(filltype,col,fur,pat);
        break; 
      case 2:
      
        if (random(1) > 0.5){
          c.drawfeathertail(feathercol);
        }else{
          c.drawfeatherfin(feathercol);
        }
        c.drawbacknotail(filltype,col,fur,pat);
        break;
       
    }
    c.drawbelly(filltype,col,fur,pat);

    if (random(1) <0.2){
      c.drawhorn(random(5,30),random(100,300),bonecol);
    }
    c.drawtongue(floor(random(2,12)));
    
    if (maind(ctype) == 2){
      c.drawbeak(bonecol);
      c.drawbirdhead(filltype,col,fur,pat);
    }else{
      
      c.drawhead(filltype,col,fur,pat);
      c.drawteeth(random(3,8),random(3,4),bonecol);
      if (random(1) <0.2){ c.drawantler(col); } if (filltype == "fur"){ c.drawear(random(0.1,0.3),random(20,120),new float[]{random(10),2,10,random(20),-10,10,random(50,80),20,50},col,fur,pat); } if (random(1)>0.5){
        c.drawtusk(random(3,10),random(3,20),random(3),random(3),bonecol);
      }
    }

    c.draweye(0.75,-c.head.tl1*random(0.1,0.8),random(15,40),new float[]{eyehue,random(30),random(80,100)},new float[]{eyehue,random(60),random(0,50)},col,fur,pat);
    if (winged > 0.8){
      c.drawwing(c.wing_a_l,c.wing_b_l,c.wing_c_l,feathercol);
    }


    switch (maind(ctype)){
      case 0: 
        c.drawforeleg_l(filltype,col,fur,pat);
        
        if (tailstyle == 0){
          c.drawhindleg_l(filltype,col,fur,pat);
        }else{
          c.drawhindlegnotail_l(filltype,col,fur,pat);
        }
        c.drawclaw(c.foreleg_l,c.forepaw_l,(clawsize+1)/2,clawbend,bonecol,col,fur,pat);
        c.drawclaw(c.hindleg_l,c.hindpaw_l,(clawsize+1)/2,clawbend,bonecol,col,fur,pat);
        
        if (sixleg == 1){
          c.drawmidleg_l(filltype,col,fur,pat);
          c.drawclaw(c.midleg_l,c.midpaw_l,(clawsize+1)/2,clawbend,bonecol,col,fur,pat);
        }
      break;case 1:
        c.drawfin_l(fincol);
        if (sixleg == 1){
          c.drawmidfin_l(fincol);
        }
      break;case 2:
        
      break;
    }

  }

}





float[] trans = new float[] {500,400};
Creature c = new Creature();
Generator g;
float[] bd;

void settings(){
  if(export){size(2000,2000);}
  else{size(800,800);}
}
void setup(){
  if(!export){noLoop();}
  colorMode(HSB,100,100,100);  
}
void draw(){
  //c.wing_c_l.a += PI/4;
  float flippy = choice(0,1);
  pushMatrix();
  g = new Generator();
  bd = new float[]{0,0,0,0};
  g.gen();
  if (flippy == 1){
    g.c.pelvis.flip();
  }
  g.c.pelvis.bounds(bd);
  if (flippy == 1){
    g.c.pelvis.flip();
  }
  background(18,4,100);
  
  float sc = min(width*0.7/(bd[1]-bd[0]),height*0.7/(bd[3]-bd[2]));
  translate(width/2-sc*(bd[1]-(bd[1]-bd[0])/2),height/2.2-sc*(bd[3]-(bd[3]-bd[2])/2));//width/2-bd[0]-(bd[1]-bd[0])/2,height/2+bd[2]+(bd[3]-bd[2])/2);
  scale(sc);
  scale(1-flippy*2,1);
  
  g.render();
  popMatrix();

  if(debug){stroke(0,100,100);g.c.pelvis.drawSkel();}
  if(export){saveFrame("render"+nf(day(),2)+nf(hour(),2)+nf(minute(),2)+nf(second(),2)+nf(millis(),3)+".png");}

}

Sketchbook

snip20161027_23 snip20161027_24

Written by Comments Off on Ngdon-Book Posted in Book

Jaqaur – Book

    GENESONG

img_3175

For my generative book project, I decided to make a song book. I called it “Genesong” (like “Genesis” or “Generate” and “Song”), and it contains 30 songs, each with rhyming lyrics, musical notes, and guitar chords. I had a lot of ideas for this book that I ended up not being able to execute, but I am very proud of how much I was able to do. I’ll go through the steps that are part of generating each song (so this is done thirty times).

song_book_flip

    GENERATION PROCESS

1. A key is chosen. I decided to limit the book’s choices to major keys, particularly Ab major, Eb major, Bb major, D major, C major, F major, and G major. This was mostly because minor keys would require different chord progressions, and would mean a lot more work and conditional statements. Throughout the book, every pitch was represented by an integer, with middle C being 0 and each half step up being an increment of 1. The keys for each song were represented by the integer of their base note, so Ab was -5, Eb was 3, etc. For this project, I disallowed changing keys mid-song.

2. A chord progression is chosen. I limited the options to progressions with four steps, just so that the music would all fit a general form. I picked progressions that are pleasant/generally popular: I-ii-IV-V , I-vi-ii-V, I-IV-V-V, I-V-vi-IV, and I-vi-IV-V.

3. A rhyme scheme is chosen. I wanted to keep these very simple, so that the rhymes can be identified despite what will be fairly nonsense lyrics. So, the only options are “ABAB”, “AAAA”, and “AABB.” The rhyme schemes affect not only the lyrics, but the music, because in general, rhyming lines in songs tend to feature parallel rhythms. That is not a rule in music, but for this project, I decided to make it one.

4. A “rhythm line” is generated. When a rhythm line is created, it is told how many beats it needs to fill. This amount will be four times the number of measures in the line (which is a random int between 3 and 5 inclusive). All songs are 4/4 time, just to keep things simple. A rhythm line knows how many elements (notes and rests) there are, how long each one lasts, and whether each element is a note or a rest. This was all randomly generated, but with a few parameters: the total length must be the length that was passed in (12, 16, or 20), the only possible lengths for elements are 0.5, 1, 2, 3, and 4 (I didn’t want to deal with dotted quarter notes, ties, etc, but as you’ll see I failed in that), the line cannot start with a rest, the line cannot end with a rest, and a rest cannot be longer than 1 beat (with such short lines and so little musical complexity, I didn’t want to make anyone sit through a long rest).

5. More rhythm lines are generated such that there are four per verse and four per chorus, and lines that rhyme have identical rhythm lines.

6. Each note is given an integer pitch based on what chord it is from (which is calculated from the key combined with where we are in the chord progression). Each line starts with the base note of said chord, just to have a nice musical landmark every few measures. Rests are given a pitch value of 100 (which is invalid).

7. Lyrics are generated via RiTa’s random word generator. I had hoped to make these coherent Markov-based sentences, but making those fit syllabically with the music and also fit in the rhyme scheme was too difficult to achieve (I tried a loop that kept re-generating lines until one worked, and would repeat this process for each song, and it often ran for several minutes with no positive results. This happened no matter what texts I put into the Markov chain, and for time’s sake I had to simplify things). So, each note gets one one-syllable word, about 80% of which are from RiTa’s lexicon, and 20% of which are randomly pieced together from an array I made of word beginnings (eg. “br”,”ch”, “st”) and word ends (eg. “ess”, “ind”, “ay”). If a word is at the end of the line, it will 100% of the time be one of the piece-meal words. Then, if it needs to rhyme with something else, it is just given the same word ending and a different beginning. When a line is complete, the first word is capitalized and a random punctuation mark is added. Random hyphens are also added (only after words that are non-lexicon) to imitate multiple-syllabled words. (Three sets of lyrics are generated for each line in the verses, one for the chorus).

8. A title of length 1-4 words is chosen from the beginning of the chorus.

An early rough-draft of a verses page. An early rough-draft of a verses page.

Once all 30 songs are generated, we have to display each one. Here are those steps.

    DISPLAY PROCESS

1. Print the title at the top of the page (if this is a verse page). Then, find your starting point (passed in as a parameter). Calculate how far apart every element in this line needs to be, based on how wide the page is and how many elements there are.

2. For each element, figure out what image you need (based on whether or not its a rest, how long the element is, and what pitch the element is). If you have room in the measure (kept track of by a measure-beat-counter–each measure needs exactly four beats), just put that image on the page, calculating its x position based on which element in the line this is, and its y position based on its pitch and the distance between the staff lines). If you do NOT have room, put down however much you have room for, draw the measure line, and then put down the rest, then draw a tie between them. This part of the code was much more difficult than I thought, because it forced me to use all the things I was trying to avoid before, like dotted quarter notes, ties, and trying to express things like “3.5 beats” with as little superfluous notation as possible. When I used multiple note symbols for one element, I sometimes had to cram them together (see below) so as not to exceed the amount of horizontal space that one element was given. I should have done more to avoid situations like this in my original generation of the rhythms. Ultimately, I accounted for this by reducing the maximum number of possible measures per line from 7 to 5, just to ensure a reasonable amount of space. Also, the lyrics for each note were added to the document at the same time as their note and at the same x position.

screen-shot-2016-10-23-at-11-08-01-pm screen-shot-2016-10-23-at-11-53-34-pm Some squished notes in early iterations of “Genesong”

3. Do step 2 for every line. Then, add things like the staff lines themselves, the treble clef, the time signature, the repeat signs, the page number, and (this was a late addition) the chord names above the lines.

4. A fun addition: add a randomly generated adverb from RiTa’s lexicon at the beginning of the piece. I did this mostly to poke fun at sheet music that demands to be played “nonchalantly” or “youthfully” or other vague terms like that. Some of the generated adverbs made some sense in context, and others (see below) did not, like “architecturally,” or “whitely.” I prefer it this way.

screen-shot-2016-10-25-at-1-49-29-am
screen-shot-2016-10-26-at-11-04-36-am
screen-shot-2016-10-25-at-1-51-04-am
screen-shot-2016-10-25-at-1-49-19-am
screen-shot-2016-10-25-at-1-49-09-am
screen-shot-2016-10-24-at-8-43-48-pm
screen-shot-2016-10-26-at-11-11-33-am
screen-shot-2016-10-25-at-1-51-34-am

Once all of the songs were displayed (actually, this is done first, but I didn’t want to wreck the narrative I had going), a table of contents is generated using the title of every song and its respective page number. This is all put together and exported as one .pdf file. The cover was made separately.

SOME MORE THINGS TO SAY

I chose not to give too much explanation in the book of what it was. On the cover, it mentions that the songs are computationally generated, but otherwise I wanted Genesong to present itself as a real song book. I tried to use fonts that conveyed a slightly old-fashioned piano book vibe, and (in theory) formatted it as if it could be “real” sheet music.

img_3177

There were a lot of ideas I had for this project that I was not able to complete in the two-week time frame, including some I mentioned above (like lyrics that had actual meaning) and some other ones (like the use of Markov chains to put more notes next to each other that “go” together). I would like to return to this project in the future, and hopefully improve upon it. One thing I would really like to add is a base clef with chords and notes to be played alongside the melody. Dynamics, fermatas, and other such musical things would be great, too. Below are some pages of my notes/plans before I began coding.

img_3188

img_3187

My original notes/plans before any coding was done.

Even as it is, my project is imperfect. Sometimes, an usually long syllable and an unusually packed line came together to equal lyrics printed on top of each other (see below). Also, the resolution did not turn out as well as I had hoped, and you can see that the notes were often a bit pixelated on the paper copy (see below). Despite these little things, I am very very proud of Genesong, and I hope to have it played and sung aloud in the near future so people who don’t read music can appreciate it!

img_3181 img_3183

Here’s a video of Professor Levin flipping through my book:

Here is a link to my code on Github: https://github.com/JacquiwithaQ/60212/tree/master/Song_Book

Here is the PDF of the iteration I chose to print: song_book-38-A-good.pdf

Here are some more/alternative iterations of the book:
song_book-22
song_book-32
song_book-36

(Here is where I would embed my code, if it weren’t 1000+ lines across 6 Processing files. Golan, let me know if you really want me to embed it anyway, and I will.)

And, finally, here is a picture of Processing being sassy with me:
img_2653

Written by Comments Off on Jaqaur – Book Posted in Book

kander – book

“Ideally We Meeta Few Times xa Week and Have Bottle Rocket Sex” juxtaposes randomly generated cartoon phalluses with sentences from “Men Seeking Women” advertisements from the Pittsburgh Craigslist. There is much comedy to be found in both the images and the sheer absurdity of many of the captions. Each combination makes a different comment on the aims of the poster: some give off an air of patheticness, some douchebaggery, some downright skeeze — highlighting the unexpectedly complex and layered meaning of a ubiquitous form of imagery in our society: the dick drawing.

The idea to draw phalluses actually came from some rather lewd commentary from one of my neighbors about my FaceOSC project. I had been wanting to do some more generative work since seeing Xastol’s generative plotter faces, and I thought the idea of little cartoon dicks would be hilarious. Additionally, right off the bat, I knew I wanted to include some sort of interesting data set or other information with comedic value. For me, having text was a must for this project — not that I couldn’t have worked with text for other projects, but all of my favorite examples from this project in particular (Death Death Death and Crazy But True Cat Stories included) were heavily reliant on text.

I first thought that I could collect data about, oddly enough, bathroom cleaning schedules (the little timesheets that they have in bathrooms of chain restaurants or gas stations that let us know when the bathroom has last been cleaned). Then, I thought I might want to caption the drawings with text messages from my ex-boyfriend, or perhaps from tweets containing a certain hashtag (I was thinking #TGIFridays).  After I made my final decision about the text, I ended up hand-selecting the Craigslist ads (although I was algorithmic in the sense that I used the most recent ones) for several reasons:

1) Craisglist doesn’t have an easily accessible API, and I didn’t have the coding prowess to figure out how to scrape Craigslist from scratch without sacrificing other elements of the project.

2) More importantly, I wanted to minimize the trolling posts that I used in the book (a few are thrown in there) because the genuine ones are much funnier.

3) Going through the posts was a hilariously good time.

Although the posts were selected manually, they were formatted computationally with Basil.js.

I’m glad I finally decided on Pittsburgh Craigslist ads — I think this project is hard to pull off if it’s not funny, and the sentences from the ads really bolster the humor.

I spent a lot of time trying to figure out the best way to draw a dick. At first, I went with the to-be expected two ellipses and a rectangle, but I realized that this approached made boring images. Once I had some of the concrete mathematical details down, I elected to create custom PShapes for the shaft and the head, and have the length, testicle diameter vary, and have the perspective vary slightly as well. Retrospectively, I would have included more variation, hairs, and maybe even warts, and if we’re really dreaming big, I might have even done the whole thing in 3d (but, at the same time, I like the cartooney quality of my result).

Click here to see the interior of the book.

Click here to see the GitHub repository for the project.

This is the original sketch for the idea, showing the variation in dimensions and orientation that I was going for.

unnamed

This was one of my first attempt to get the alignment right with ellipses and a rectangle

prelim_program

This is a later attempt using lines. I kind of like where this one was going — I think if I had adapted it to use curves, it could have also turned out nice.

prelim_program2

A fairly run-of-the-mill representative of the final product

image-19


Here’s a video shot by the professor, flipping through the book:

Below is the code for each respective element:

Image generation:

PShape d;
PShape b;
PShape h;
float theta;
float theta2;
float x;
float y;
float xHold;
float yHold;
float offSetX;
float offSetY;
float scale;
float yScale;
float len;
float isFlipped;
float translation;
float headWidth;
float headHeight;
float girth = random(3, 7);
color d1;
color d2;
int page_num = 0;

void setup() {
  background(255);
  size(550, 550);
  frameRate(30);
}

void draw() {
  background(255);
  stroke(0, 200);
  isFlipped = random(1, 3);
  yScale = random(15, 35);
  len = random(20, 200);
  scale = random(1, 1.2);
  float col = random(1, 4);
  if (col < 2) { setColorsPinks(); } if (col >=2 && col < 3) {
    setColorsBrowns();
  } else {
    setColorsTans();
  }
  //background(100);
  translation = random(30, 50);
  translate(width/2, height/2);
  float angle = random(-PI/2, PI/2);
  rotate(angle);
  translate(-width/2, -height/2);
  if (isFlipped <= 2) {
    translate(width/2, height/2);
    scale(-1, 1);
    translate(-width/2, -height/2);
  }
  drawDick(d1);
  translate(width/2, height/2);
  scale(-scale, scale);
  translate(translation, 0);
  translate(-width/2, -height/2);

  drawDick(d2);

  translate(-translation/2, 5);
  drawHead(d2);
  for (int j = 0; j < random(2, 3); j++) {
    stroke(0);
    drawVeins();
  }
  if(page_num < 30){
    saveFrame("image-" + str(page_num) +".png");
  }
  page_num += 1;
}

void setColorsPinks() {
  d1 = color(random(240, 255), random(210, 225), random(160, 180), 220);
  d2 = color(random(240, 255), random(210, 225), random(160, 180));
  //head = color(random(240, 255), random(210, 225), random(160, 180));
}

void setColorsBrowns() {
  d1 = color(random(75, 85), random(40, 47), random(0, 30), 220);
  d2 = color(random(75, 85), random(40, 47), random(0, 30));
  //head = color(random(75, 85), random(40, 47), random(0, 30));
}

void setColorsTans() {
  d1 = color(random(195, 205), random(150, 160), random(110, 115), 220);
  d2 = color(random(195, 205), random(150, 160), random(110, 115));
  //head = color(random(195, 105), random(150, 160), random(110, 115));
}

void drawDick(color c) {
  stroke(0, 200);
  d = createShape();
  d.beginShape();
  d.strokeWeight(2.5);
  d.fill(c);
  x = width/2;
  y = height/2;

  d.vertex(x, y);
  x = x - girth;
  y = y + len;
  d.vertex(x, y);
  xHold = x + 20*cos(-PI/2) + girth/2;
  yHold = y + 17;
  for (int i = 0; i < 100; i++) {
    theta = map(i, 0, 99, -PI/2, PI);
    x = xHold + 30*cos(theta);
    y = yHold + yScale*sin(theta)*1.25;
    d.vertex(x, y);
  }
  x = x - girth;
  y = height/2;
  d.vertex(x, y);
  d.endShape();
  shape(d);
}

void drawHead(color c) {
  stroke(0, 200);
  headHeight = random(35, 50);
  h = createShape();
  h.beginShape();
  h.fill(c);
  h.strokeWeight(2);
  h.vertex(width/2, height/2);
  h.vertex(width/2 - 20, height/2);
  for (int i = 0; i < 100; i++) {
    theta = map(i, 0, 99, 52*PI/50, 98*PI/50);
    x = width/2 + 27*cos(theta);
    y = height/2 + headHeight*sin(theta);
    h.vertex(x, y);
  }
  h.vertex(width/2 + 20, height/2);
  h.vertex(width/2, height/2);
  h.endShape();

  shape(h);
  stroke(0);
  float ay = height/2 + headHeight*sin(3*PI/2);
  float divLen = random(10, 20);
  stroke(0, 100);
  line(width/2, ay, width/2, ay + divLen);
}

void drawVeins() {
  stroke(0);
  strokeWeight(2);
  float ax = random(width/2 - 5, width/2 + 10);
  float ay = random(height/2, height/2 + len);
  line(ax, ay, ax, ay + random(10, (height/2 + len - ay)));
}

Text generation:

import rita.*;
String entries[] = new String[30];
JSONObject json;

void setup() {
  //basically just initializes all the craiglist entries I'll be using

  entries[0] = "Im in search of a beautiful young woman between the age of does not matter-23 who is seeking an older man for a relationship and to treat her like a princess inside and outside of the bedroom. She should be tall have long hair long legs and a gorgeous butt and on birth control, highly recommended but not a deal breaker. She will receive alot of presents and affection . I am very serious about treating a very special young lady this way and if your interested I expect you to be very serious about accepting. SERIOUS REPLIES ONLY. Send a full clothed body pic with face along with stats. The closer you are to me the more you will be spoiled. Your pic will get mine once you prove your serious enough.";
  entries[1] = "I have a big, roomy Suv and I've got a taste kink. Any women out there feel the same way? I've got all the time in the world today and I'm looking to have some fun. Put your favorite color in the subject line so I know you're not a bot. Let's chat a little bit and see what we'd like to do on this dreary day.";
  entries[2] =
    "I think it's finally reached the point where i should throw in the towel with craigslist. I get "
    +"almost no replies from real people. I used to at least get the occasional interesting reply or two." 
    +" I'm bored. Are you tired of the same old/same old too? Let's talk and take it from there. I'm " 
    + "pretty open-minded even if that means we just talk. Please reply with a picture and a little about"
    + " yourself. Excuse the cheesy pictures, but I'm not putting my face on cl so I figured these were" 
    + " better than nothing. Note: Someone stole one of my pics and is using it to post ads. If you see an"
    + " ad with my picture talking about bi-guys, bi-girls, and hsv...know that it is NOT me. I am not bi."
    + " I do not have hsv. Nothing in the ad is true about me. That is someone using my picture without my"
    + " permission.";

  entries[3] = "Hi! Is anyone else bored tonight? It's so nice outside. Anyways... I'm new-ish to the area and have"
    + " been single for about a year. I miss cuddling and making out. Anyone up for a movie late tonight? I" 
    + " am. Who me? 33, white, attractive, 6'1, financially stable, four twenty friendly, movie lover," 
    + " crazy about kissing.You? Pic for pic! Yours gets mine." 
    + " Ps you can pick the movie.";

  entries[4] = "Swm, 6' tall, average looks, good body, tight tush, great sense of humor, will buy dinner for any" 
    + " female on one condition: you wear a skirt and hose/nylons. I bet I will be eating alone tonight.";

  entries[5] = "Oh it has a first name!!"
    + " Looking for a fwb scenario so my kabossy can fit that bun!!"
    + " Ideally we meeta few times xa week and have bottle rocket sex."
    + " I have skills that will blow you're mind."
    + " In fact if you don't get with me you're missing out on the treat of a lifetime." 
    + " My kabossy is in charge!!";

  entries[6] = "hello , Im 33 yo white male , 6ft1 180lbs, SINGLE ,in good shape,two part time jobs,no kids,been" 
    + " told im goodlooking.looking for a Long term relationship . I have my own place and i drive.. I am a" 
    + " country/redneck/metalhead type guy all wrapped in one...some fav bands...Misfits,Metallica,Social" 
    + " D, Dropkick Murphys, AC-DC, Disturbed, Fear Factory, and thats all i can think of right now.I have" 
    + " 4 tattoos, one on right bicep and 3 on left forearm, id like to get more in the future...I am" 
    + " hoping to meet the right one in 2016 since its been a few years since ive had a relationship...Not"
    + " looking for liars , whores , wana be gangsters , gold diggers , game players ,and drama queens, or" 
    + " women still attached to their ex.";

  entries[7] = "Me:"
    + " I am tall, in shape, and intelligent. I hear I am easy on the eyes, and have all of my teeth. I"
    + " have a decent job and my own place, although I am new to the area. I am 420 friendly, but other" 
    + " wise drug free. I am disease and drama free as well and have no kids or anything."
    + " You:"
    + " Honesty is pretty crucial."
    + " Cute although you dont exactly have to think so haha"
    + " Smart"
    + " Job, car, school, art, or something you do with your life"
    + " 420 friendly is a plus"
    + " Piercings and tattoos are also a plus"
    + " Disease/kid/drama free";

  entries[8] = "Anyone up late this evening and want to chat and see where it leads? Maybe make a friend or maybe" 
    + " something more? Send me an email so we can chat! I am open to any age, relationship status, race," 
    + " etc. as long as you are female lol so don't be shy :)";

  entries[9] = "Anybody feel like hangin out and having good conversation? We could talk about anything you feel" 
    + " like.";

  entries[10] ="I'm white male regular build not bad looking. I have no tats, no piercings, never married, no kids." 
    + " No crazy friends, no crazy ex's. I'm more of a homebody and stay out of trouble. I like going out "
    + " to all depending on what to do. I'm looking for someone to talk too who is just a normal regular "
    + " person,"
    + " I'm friendly and nice. Down to earth and chill. If you wanna know more..."
    + " Put 'email' in subject line or send me a text"
    + " FOUR ONE TWO NINE FIVE ONE FOUR FIVE SEVEN TWO";


  entries[11] = "Hey ladies, I guess I just wanna keep it quick and to the point lol. I'm not looking for a"
    + " relationship or anything serious, just a girl to hangout and mess around with on occasion. If you"
    + " would be interested in that kind of thing reply to my post and let's see what happens. Please"
    + " attach a picture of yourself and put your boob size in the subject line so I know you're legit.";

  entries[12] = "I am not picky tonight. Invite me over already. I can travel to you if things feel right.";

  entries[13] = "Hi There!! I am from California... I live in San Diego, I've always wanted to travel but I haven't" 
    + " been here :/ only through CL haha... But I don't want to go without finding someone who I can trust"
    + " and who can show me around so I have a good time :)"
    + " Possibly to meet and have a connection."
    + " About Me: I'm a chubby white guy, long blond hair and blue eyes :)" 
    + " I'm 27, have a good job and I'm easy going."
    + " You can email me or KIK me at"
    + " Tcandee";

  entries[14] = "Contact me tonight....discreet fun wanted..I'm in shape,sense of humor, 40s..text 'I'm in' to"
    + " fouronetwothreeeightninetwoonefivesix. Married / attached ,couples welcome";

  entries[15] = "Howz about a nice collaboration between friends? We can have pizza pie as I spice it up with my" 
    + " sausage as the perfect ingredient.";

  entries[16] = "Hey anybody want to get together tonight? We can catch a movie or do oral? We can Iggy wiggy liol.";

  entries[17] = "I am at the point where making time to slowdown, relax and enjoy is important, I like the" 
    + " interaction of dinning in, dinning out, the connection created by preparing and sharing a meal" 
    + " together, an evening set aside for a nice wine, conversation, candles, teasing, pleasing and an" 
    + " antipasto to nibble on. Lol";

  entries[18] = "Laughter is important, my sense of humor can range from playful to sarcastic. Not looking to change" 
    + " my life or anyone else's other than filling the void. Passionate, expressive and looking for the"
    + " same. Definitely not an out of sight out of mind person, I like to keep in touch throughout the" 
    + " day, share thoughts, feelings and events. For me the mental connection is important and" 
    + " communication is key. I don't mean to offend anyone but please be local to the Pittsburgh area," 
    + " over 45, ready to slowdown and enjoy, willing and able to make time to do so and looking to share" 
    + " and enjoy plenty of pleasure, passion and fun. If interested message me, put your favorite wine in" 
    + " the subject line, please include a photo, what you're looking for, what you enjoy/find pleasure in" 
    + " and PLEASE!!! Be serious and looking for something regular and ongoing, the more we have to build" 
    + " on the hotter it will get. Thank you.";

  entries[19] = "would love to be a live in companion and a lover to a naughty older woman 60-85,any race," 
    + " religion. I am an east Indian male NY raised 55 years old and an American citizen. I don't have" 
    + " Indian accent when I speak English and I have an MBA. I need a place to stay. I will caress you" 
    + " allover, give full body massages, give you great sex, cook and your chores. Please respond with" 
    + " your phone number.";

  entries[20] = "For reals. I love breast play. Yours, not mine lol. Hoping to find someone out there that loves" 
    + " having your nipples sucked. Such a turn on for me. Well and kissing too." 
    + " I can drive or host. Anyone curious? Please include a face picture of yourself and I'll return the" 
    + " favor. Thanks!";

  entries[21] = "Ok who on this site really wants to meat? Let's meat today and meat consistently if that's okay. So" 
    + " let's do this.";

  entries[22] = "I'm a nightime person who likes to stay up past midnight. Any women on here looking to chat who" 
    + " likes to stay up past midnight? If so put midnight in subject line";

  entries[23] = "Looking for guys and girls who want to throw down some fun. We got the dunking stix so you supply" 
    + " the honey buns.";

  entries[24] = "So as I have stated just out of a bad relationship. My ex just could not understand my experiments" 
    + " with men. So maybe you will understand? Hey we all have needs right?";

  entries[25] = "I'm looking for a nice young thick girl to spoil and give gifts to. This can be one time or" 
    + " ongoing. Please no bbw. Please reply with favorite color in subject. Also send pics and a phone #";

  entries[26] = "This is a long shot I know. And I know most if not all of you that read this will judge and form" 
    + " your opinions. That's okay. Hopefully there is one woman out there that is adventurous and open" 
    + " minded enough or maybe in a similar situation that they understand."
    + " I guess I will just lay it all out on the table. I am a 54 yr old married man. Due to wife's lack" 
    + " of interest, there hasn't been much in the way of 'sex' for quite some time. My release comes from," 
    + " you guessed it, masturbation. I am looking for a woman who is possibly in the same situation that" 
    + " might be interested in getting together for some mutual masturbation. Not looking for intercourse." 
    + " We could watch each other or we could lend a hand and help each other. The choice would be yours." 
    + " Absolute discretion is a must !! Prefer you be over 40 yrs old. Married or single. If this sounds" 
    + " like something you would like to possibly discuss, please send me an email and tell me your" 
    + " thoughts on the subject and we can go from there. Women Only Please !!!!";

  entries[27] = "Are there any cute girls out there who might be interested in a tribute? If you don't know what" 
    + " that is it is when a girl sends a sexy pic or 2 to a guy who then drops his load on the picture and" 
    + " takes a picture of that and sends it back to the girl."
    + " It's just a fun flirty activity and doesn't require any personal meets or anything like that. If" 
    + " this is something you'd be interested in please send me a pic and what you're looking for and" 
    + " please put tribute in the title so I know you're real."
    + " I'm real -- today is Friday and has been sunny but cooler but the weekend will warm up nicely.";

  entries[28] = "I am a mature, married busy workaholic & needing assistance with stress relief from time to time. I" 
    + " am seeking a steady & reliable massage therapist( don't worry about experience, I can teach you the" 
    + " techniques) who can host with great hands for a good sensual full body massage at least once a" 
    + " week. No need to look for a p/t job, do more with the time saved and let's help each other out." 
    + " Deal with one sane and decent guy, no need to hook up with random men. Ideal candidate would be a" 
    + " woman who is uninhibited with a great sense of humor and understands the need for privacy. Looking" 
    + " to start soon. This is a perfect opportunity for stay at home mom (after the kids are at school)" 
    + " retired woman( age is not an issue), college student or any woman looking to supplement her income." 
    + " I have a flexible schedule which will enable us to work around yours. Please put relief in subject"
    + " line so I will know that you are real and sincere.";


  entries[29] = "Quit flagging and sending racist emails, haters. Live and let live."
    + " Some guys like blondes. Some guys like BBWs. I prefer ladies who have a darker skin tone. Don't ask" 
    + " me why. Don't know. Don't care. And it isn't just a phase. I haven't dated a white woman in 10"
    + " years. I'm not looking for anything serious (though I definitely want to keep the option open) nor" 
    + " am I interested in anything financial including a SD/SB arrangement."
    + " What I'm seeing is a situation where we get dinner sometimes, or a happy hour. We catch a movie," 
    + " hear some live music. And there is lots of naked time. We aren't up under each other all the time," 
    + " but we're friends, confidants and lovers. I'm college educated, been told I have a good sense of" 
    + " humor and can usually keep up on the conversation."
    + " Anyway, if that has a beat for you, drop me a note. Put 'Maybe I'm the one' in the subject and"
    + " lets's get coffee."
    + " Please make the subject of your reply something interesting so i know ur not a dbag or spam bot."
    + " Thanks.";

  json = new JSONObject();
  for (int i = 0; i < 30; i++) {
    String result[] = RiTa.splitSentences(entries[i]);
    String sent = result[int(random(0, result.length))];
    json.setInt("id", i);
    json.setString("sentence", sent);
    String add = str(i);
    saveJSONObject(json, "data/text" + add+ ".json");
  }
}

Scripting Illustrator with Basil.js:

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";

var jsonString = b.loadString("text.json");
var jsonData;

//--------------------------------------------------------
function setup() {

  // Clear the document at the very start. 
  b.clear (b.doc());

  // Make a title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Helvetica","Light"); 
  b.textAlign(Justification.LEFT_ALIGN); 
  b.text("Ideally we meeta few times xa week and have bottle rocket sex", 72,72,360,36);
  b.text("Katie Tender", 72,108,360,36);

  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);

  var titleX = 72; 
  var titleY = 72;
  var titleW = 72;
  var titleH = 72;

  var captionX = 72; 
  var captionY = b.height - 108;
  var captionW = b.width-144;
  var captionH = 36;

  var imageX = 72*1.5; 
  var imageY = 72; 
  var imageW = 72*4.5; 
  var imageH = 72*4.5; 

  for (var i = 0; i < jsonData.length; i++) {
 
    b.addPage();

    b.noStroke();  // no border around image, please.
    var anImageFilename = "images/" + jsonData[i].image;
    var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
    anImage.fit(FitOptions.PROPORTIONALLY);

    b.noStroke(); 
    b.fill(b.random(180,220),b.random(180,220),b.random(180,220)); 
    b.ellipseMode(b.CORNER);
    b.ellipse (titleX,titleY,titleW,titleH);

    b.fill(255);
    b.textSize(56);
    b.textFont("Helvetica","Bold"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text(jsonData[i].title, titleX,titleY,titleW,titleH);

    b.fill(0);
    b.textSize(36);
    b.textFont("Helvetica","Regular"); 
    b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN );
    b.text(jsonData[i].caption, captionX,captionY,captionW,captionH);

  };
}

b.go(); 

takos-book

Final Product:

Attempts At Jokes Printed Version PDF
Link to Twitter bot version

14885807_1317431678280588_2032725612_n14877263_1317431571613932_1400395020_n
14914742_1317431461613943_1562010121_n14881410_1317431348280621_2078194419_o

Video of the professor flipping through my book:


Process:
My joke book is a one hundred and twenty five pages, with three hundred and sixty six jokes, with three jokes per page, an intro page – which gives a one sentence blurb on what the contents are, and a title page. Its printed on cream paper and machine bound. It is a collection of jokes based on tweets that where posted at around the same time and combined into a question and answer format to achieve a somewhat coherent, sometimes humorous joke.

For my generative book I wanted I wanted to do something with jokes. I wanted to follow a well known joke format so that they could be somewhat identifiable,the original thing that came to mind was “why did the chicken cross the road?” But I thought that was to specific so I generalized it to just a “why did what is and the idea that I thought up to do it was to combine different tweets into question and answer format. I then erased everything before the word “before” and before “why did” in the question and answer. I had a hard time getting the tweets and the author text to not overlap in the pdf, but overall i think it worked successfully, and I’m happy with the result of using the cream paper instead of white paper even though I’d originally had my doubts about it. I don’t like my joke separator choice of using “-” and think it would have looked better with a thinner version, or simply without one. I am happy with the results of my generator, and think that the twitter version allows me to edit the code to see possible outcomes in a more successful way, and look forward to developing it further.

(note: I didn’t include a picture from my sketchbook because i did not do any image based work.)

The first of my Processing programs uses the Temboo API to find tweets that contain the phrase “Why did”. The second code retrieves tweets with “Because”. The third takes the tweets and gathers the relevant data and also cleans up the text to isolate the relevant parts. The 3rd program below is my inDesign (Basil.js) script, which prints 3 jokes per page. My last program combines the first 3 codes and cleans them up differently in order to match up to the 140 character limit on twitter, and then uses Temboo to tweet the valid ‘jokes’ every time i run my program.

Code:
Processing:

This code retrieves tweets contain the phrase “why did”

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;

// Create a session using your Temboo account application details
TembooSession session = new TembooSession("takos", "myFirstApp", REDACTED);
int x = 209;
void setup() {
  // Run the Tweets Choreo function
  runTweetsChoreo();
}

void runTweetsChoreo() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);
  tweetsChoreo.setResultType("recent");

  // Set inputs
  String myQuery  = "\"why did\"";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken(REDACTED);
  tweetsChoreo.setConsumerKey(REDACTED);
  tweetsChoreo.setConsumerSecret(REDACTED);
  tweetsChoreo.setAccessTokenSecret(REDACTED);

  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();
  
  // Print results
  println(tweetsResults.getLimit());
  println(tweetsResults.getRemaining());
  println(tweetsResults.getReset());
  //println(tweetsResults.getResponse());

  String[] result = {tweetsResults.getResponse()};
  //println(result);
  String name =  "..\\data\\wdt" + x + ".json";
  saveStrings(name,result);
}

This code retrieves tweets that contain the word “because”

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;

// Create a session using your Temboo account application details
TembooSession session = new TembooSession("takos", "myFirstApp", REDACTED);
int x = 209;
void setup() {
  // Run the Tweets Choreo function
  runTweetsChoreo();
}

void runTweetsChoreo() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);
  tweetsChoreo.setResultType("recent");
  // Set inputs
  String myQuery  = "\"because\"";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken(REDACTED);
  tweetsChoreo.setConsumerKey(REDACTED);
  tweetsChoreo.setConsumerSecret(REDACTED);
  tweetsChoreo.setAccessTokenSecret(REDACTED
  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();
  
  // Print results
  println(tweetsResults.getLimit());
  println(tweetsResults.getRemaining());
  println(tweetsResults.getReset());
  //println(tweetsResults.getResponse());

  String[] result = {tweetsResults.getResponse()};
  println(result);
  String name = "..\\data\\bc" + x + ".json";
  saveStrings(name,result);
  //saveStrings(myQuery+".json",result);
}

This code strips both tweets and saves the relevant text selection of “Why” & “Because” into a JSON file with other rewlevant data from the tweets, including Authors and time.

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;

JSONArray tweets;
int number = 201;

void setup() {
  String aJsonFilename = "wdt" + number + ".json";
  JSONObject json = loadJSONObject(aJsonFilename);
  
  String bJsonFilename = "bc" + number + ".json";
  JSONObject bjson = loadJSONObject(bJsonFilename);
  
  tweets = new JSONArray();
  if (json == null || bjson ==null) 
  {
    println("JSONObject could not be loaded!");
  }
  else 
  {
    JSONArray statuses = json.getJSONArray("statuses");
    JSONArray statusesb = bjson.getJSONArray("statuses");
    for (int i=0; i<statuses.size(); i++) { JSONObject aTweet = statuses.getJSONObject(i); String aText = aTweet.getString("text"); String aTime = aTweet.getString("created_at"); JSONObject aUser = aTweet.getJSONObject("user"); String aScreenName = aUser.getString("screen_name"); String aName = aUser.getString("name"); String aColor = aUser.getString("profile_background_color"); println(i + "\t" + aText); JSONObject tweet = new JSONObject(); tweet.setString("text", aText +" "); tweet.setString("name", aName); tweet.setString("color", aColor); tweet.setString("screenName",aScreenName); tweet.setString("date", aTime); //tweets.setJSONObject(i, tweet); JSONObject bTweet = statusesb.getJSONObject(i); String bText = bTweet.getString("text"); String bTime = bTweet.getString("created_at"); JSONObject bUser = bTweet.getJSONObject("user"); String bScreenName = bUser.getString("screen_name"); String bName = bUser.getString("name"); String bColor = bUser.getString("profile_background_color"); println(i + "\t" + bText); tweet.setString("textB", bText +" "); tweet.setString("nameB", bName); tweet.setString("colorB", bColor); tweet.setString("screenNameB",bScreenName); tweet.setString("dateB", bTime); tweet.setString("credits", "By " + aName + " and " + bName + "."); tweets.setJSONObject(i, tweet); tweet.setString("question", getQuestion(aText)+"?"); tweet.setString("answer",getAnswer(bText)); tweet.setString("by",aName + " & " + bName ); saveJSONArray(tweets, number + ".json"); } }} String getQuestion(String text) { int index = (text.toLowerCase()).indexOf("why did"); String cleared = text.substring(index); if((cleared.toLowerCase()).indexOf("http") > 0){
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("http")));}
  if((cleared.toLowerCase()).indexOf("?") > 0){
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));}
    
  return cleared; 
}

String getAnswer(String text)
{
  String lowercasedText = text.toLowerCase();
  int indexOfBecause = lowercasedText.indexOf("because");
  String textStartingFromBecause = text.substring(indexOfBecause);
  String textStartingFromBecauseLowercased = textStartingFromBecause.toLowerCase();
  if (textStartingFromBecauseLowercased.indexOf("http") > 0){
    return text.substring(indexOfBecause, (lowercasedText.indexOf("http")));
  }
  return text.substring(indexOfBecause); 
}
  

Indesign Code:

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";

var jsonString = b.loadString("all-2.json");
var jsonData;

//--------------------------------------------------------
function setup() {
    
  b.clear (b.doc()); // Clear the document at the very start. 
  var pageNum = 0;    
  var pgW = 6*72;        
  var pgH= 9*72;

  var doc=b.doc();
  with(b.doc().documentPreferences) {
    pageWidth = pgW + "pt";
    pageHeight = pgH + "pt";
    b.width = pgW;
    b.height = pgH;
  }
    

  // Make a title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Helvetica","Light"); 
  b.textAlign(Justification.RIGHT_ALIGN ); 
  b.text("Attempts at Jokes", 72,72,360,36);
  b.textSize(12);
  b.text("Takos & Twitter", 72,108,360,36);
  
  // Parse the JSON file into the jsonData array
  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);

  // Initialize some variables for element placement positions.
  // Remember that the units are "points", 72 points = 1 inch.
  var titleX = 36; 
  var titleY = 76;
  var titleW = 72*5;
 
  var titleH = 24;

  // These are the header fields from the tweet CSV:
  // tweet_id,in_reply_to_status_id,in_reply_to_user_id,timestamp,source,text,
  // retweeted_status_id,retweeted_status_user_id,,retweeted_status_timestamp,
  // expanded_urls

  var textW=pgW*0.75;
  var textOffs=(pgW-textW)/2;
  var textOffsY=72;
  var x=textOffs;
  var y=pgH*0.25;
    
  var textSize1=8;
  var textSize2=30;
  var tweetDist=textSize2*2;
    
  var whiteSpaceStr=" ";

  var elements=[];
    
  var doNewPage=false;
  var w = 0;
  // Loop over every element of the book content array
  for (var i = 0; i <jsonData.length; i+=3){ // 10; i+=3){ //jsonData.length; i+=3) { // Create the next page. b.addPage(); b.noStroke(); // no border around image, please. b.fill(0,0,0); var textY = titleY; var fontA = "Itallic"; var fontB = "Regular"; var size1 = 14; var size2 = 8; var font = "Helvetica"; pageNum = pageNum +1; b.textSize(size1); b.textAlign(Justification.CENTER_ALIGN); b.textFont(font,fontB); b.text("-", 216-20, 228, 40, 40); b.text("-", 216-20, 420, 40, 40); // b.textSize(size2); // b.text(pageNum, 216-20, 612, 40, 40); b.textSize(size1); w = 0; if (jsonData[i].question.length > 57){
        w = 15;
    }
    if (jsonData[i].question.length > 114){
        w = 30;
    }  
    b.color(0,0,0);    
    b.textFont(font,fontB); 
    b.textAlign(Justification.LEFT_ALIGN );
    b.text(jsonData[i].question, titleX,textY,titleW,titleH*3); textY+=titleH;//+15; 
      
    b.color(80,80,80);    
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i].name,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;
    
      
          
    w =0; 
      
    if (jsonData[i].answer.length > 57){
        w = 15;
    }
    if (jsonData[i].answer.length > 114){
        w = 30;
    }  
        
    b.color(0,0,0);      
    b.textSize(size1);
    b.textFont(font,fontB); 
    b.textAlign(Justification.RIGHT_ALIGN );
    b.text(jsonData[i].answer, titleX,textY,titleW,titleH*3); textY+=titleH;//+15;
    b.color(80,80,80);
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i].nameB,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5; 
   
      
    w = 0;
    if (jsonData[i+1].question.length > 57){
        w = 15;
    }
    if (jsonData[i+1].question.length > 114){
        w = 30;
    }    
    b.color(0,0,0);
    textY = (pgH-2*titleY)*(1/3)+titleY*1.5;
    b.textSize(size1);
    b.textFont(font,fontB); 
    b.textAlign(Justification.LEFT_ALIGN );
    b.text(jsonData[i+1].question, titleX,textY,titleW,titleH*3); textY+=titleH;//+15; 
    b.color(80,80,80);      
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i+1].name,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;//2;
    
      
    w =0; 
      
    if (jsonData[i+1].answer.length > 57){
        w = 15;
    }
    if (jsonData[i+1].answer.length > 114){
        w = 30;
    }  
      
    b.color(0,0,0);      
    b.textSize(size1);
    b.textFont(font,fontB);
    b.textAlign(Justification.RIGHT_ALIGN );
    b.text(jsonData[i+1].answer, titleX,textY,titleW,titleH*3); textY+=titleH;//+15;
    b.color(80,80,80);
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i+1].nameB,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;//2; 
    w = 0;
    if (jsonData[i+2].question.length > 57){
        w = 15;
    }
    if (jsonData[i+2].question.length > 114){
        w = 30;
    }
    b.color(0,0,0);      
    textY = (pgH-2*titleY)*(2/3)+titleY*2;
    b.textSize(size1);
    b.textFont(font,fontB); 
    b.textAlign(Justification.LEFT_ALIGN );
    b.text(jsonData[i+2].question, titleX,textY,titleW,titleH*3); textY+=titleH;//+15; 
    b.color(80,80,80);      
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i+2].name,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;//2;
    
    w =0; 
      
    if (jsonData[i+2].answer.length > 57){
        w = 15;
    }
    if (jsonData[i+2].answer.length > 114){
        w = 30;
    }
    b.color(0,0,0);      
    b.textSize(size1);
    b.textFont(font,fontB); 
    b.textAlign(Justification.RIGHT_ALIGN );      
    b.text(jsonData[i+2].answer, titleX,textY,titleW,titleH*3); textY+=titleH;//+15;
    b.color(80,80,80);
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i+2].nameB,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;//2; 
 

  };
}

// This makes it all happen:
b.go(); 

Code For Twitter Bot

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;
import com.temboo.Library.Twitter.Tweets.*;
TembooSession session = new TembooSession("takos", "myFirstApp", "");

JSONArray tweets;
String finAnswer;
String finQuestion;
String finCredits;
String mode = "recent";

void whyDid() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);
  tweetsChoreo.setResultType(mode);

  // Set inputs
  String myQuery = "\"why did\"";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken("-");
  tweetsChoreo.setConsumerKey("");
  tweetsChoreo.setConsumerSecret("");
  tweetsChoreo.setAccessTokenSecret("");

  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();

  String[] result = {tweetsResults.getResponse()};
  String name = "wdt.json";
  saveStrings(name, result);
}

void because() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);
  tweetsChoreo.setResultType(mode);
  // Set inputs
  String myQuery = "because";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken();
  tweetsChoreo.setConsumerKey();
  tweetsChoreo.setConsumerSecret();
  tweetsChoreo.setAccessTokenSecret();

  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();

  String[] result = {tweetsResults.getResponse()};
  String name = "bc.json";
  saveStrings(name, result);
}

void tweetIt() {
  StatusesUpdate statusesUpdateChoreo = new StatusesUpdate(session);
  String currentTweet = finQuestion + "\n" + "\n" + finAnswer + "\n" + "\n" + finCredits;

  if (currentTweet.length() <
  = 140) {
    statusesUpdateChoreo.setStatusUpdate(currentTweet);
    statusesUpdateChoreo.setAccessToken();
    statusesUpdateChoreo.setConsumerKey();
    statusesUpdateChoreo.setConsumerSecret();
    statusesUpdateChoreo.setAccessTokenSecret();

    StatusesUpdateResultSet statusesUpdateResults = statusesUpdateChoreo.run();
    //println(statusesUpdateResults.getResponse());'
  } else
  { 
    println(currentTweet);
  }
}

void setup() {

  whyDid();
  because();
  String aJsonFilename = "wdt.json";
  JSONObject json = loadJSONObject(aJsonFilename);

  String bJsonFilename = "bc.json";
  JSONObject bjson = loadJSONObject(bJsonFilename);

  tweets = new JSONArray();
  if (json == null || bjson ==null)
  {
    println("JSONObject could not be loaded!");
  } else
  {
    JSONArray statuses = json.getJSONArray("statuses");
    JSONArray statusesb = bjson.getJSONArray("statuses");
    for (int i=0; i< statuses.size(); i++) { 
      JSONObject aTweet = statuses.getJSONObject(i); 
      String aText = aTweet.getString("text"); 
      String aTime = aTweet.getString("created_at"); 
      JSONObject aUser = aTweet.getJSONObject("user"); 
      String aScreenName = aUser.getString("screen_name"); 
      String aName = aUser.getString("name"); 
      JSONObject bTweet = statusesb.getJSONObject(i); 
      String bText = bTweet.getString("text"); 
      String bTime = bTweet.getString("created_at"); 
      JSONObject bUser = bTweet.getJSONObject("user"); 
      String bScreenName = bUser.getString("screen_name"); 
      String bName = bUser.getString("name"); 
      finAnswer=getAnswer(bText); 
      finQuestion =getQuestion(aText); 
      finCredits = aScreenName + "&" + bScreenName; 
      Cleanup(); 
      tweetIt();
    }
  }
} 
void Cleanup() { 
  finAnswer=endsInAt(clearAmp(finAnswer)); 
  finQuestion =endsInAt(clearAmp(finQuestion)) + "?"; 
  finCredits = clearQ(finCredits);
} 
String getQuestion(String text) { 
  int index = (text.toLowerCase()).indexOf("why did"); 
  String cleared = text.substring(index); 
  if ((cleared.toLowerCase()).indexOf("http") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("http")));
  }
  if ((cleared.toLowerCase()).indexOf("?") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));
  }
  if ((cleared.toLowerCase()).indexOf("\n") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));
  }
  return cleared;
}

String getAnswer(String text)
{
  int index = (text.toLowerCase()).indexOf("because");
  String cleared = text.substring(index);
  if ((cleared.toLowerCase()).indexOf("http") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("http")));
  }
  if ((cleared.toLowerCase()).indexOf("...") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("...")));
  }
  if ((cleared.toLowerCase()).indexOf("\n") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));
  }
  if ((cleared.toLowerCase()).indexOf("!") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));
  }

  return cleared;
}

String clearAmp(String input) {
  int index = input.indexOf("&");
  while (index != -1) {
    input = input.substring(0, index) + input.substring(index+5, input.length());
    index = input.indexOf("&");
  }
  index = input.indexOf(">");
  while (index != -1) {
    input = input.substring(0, index) + input.substring(index+4, input.length());
    index = input.indexOf(">");
  }
  return input;
}

String endsInAt(String input) {
  int index = input.indexOf("@");
  if (index>
  -1 &&
  (input.substring(index, input.length())).indexOf(" ") > -1) {
    input = input.substring(0, index);
  }
  return input;
}

String clearSpace(String input) {
  int index = input.indexOf(" ");
  while (index != -1) {
    input = input.substring(0, index) + input.substring(index+2, input.length());
    index = input.indexOf(" ");
  }
  return input;
}

String clearQ(String input) {
  int index = input.indexOf("?");
  while (index != -1) {
    input = input.substring(0, index) + input.substring(index+2, input.length());
    index = input.indexOf("?");
  }
  return input;
}

Written by Comments Off on takos-book Posted in Book

Lumar-BookOfKindness

BOOK DESCRIPTION: “Something Kind” by Lumar is a book that is computationally generated, leveraging human computation through Amazon Mechanical Turk, several short programs in Processing, and Basil.js.

The text of the book consists of responses provided by Amazon Mechanical Turk workers, when they were asked to “write about something unexpectedly nice that someone did” for them. The program incorporates the RiTa library for content analysis, and singles out the pertinent action verbs of each quote to display on the facing page spread. Altogether the book emphasizes kindness as an action – and ends the book with the actions “GO & DO” with some gradual play on flipbook transitions through both the heart imagery and verb spread/sizing.

bookkind

_dsc2169-copy

_dsc2170-copy

_dsc2171-copy

The action verbs on the page spreads all eventually get redder, smaller and darker the closer the book gets to the end with the last verb page being “GO & DO” in smaller font at the bottom of the page.

screen-shot-2016-10-28-at-4-53-15-pm

I ended the book with a screenshot of an email a Turker sent me thanking me for the HIT (task)! Wow! I was touched! I’m really glad she liked it!screen-shot-2016-10-28-at-4-53-25-pm

 

REGRETS: Besides not being able to execute some parts of the original idea (the original sequence held more of a sentiment of a personalized gift of kind moments book) I had really wanted the extracted verbs to be written in a different color directly within the quote. I had gotten the program to send to the json file the index values of the words I wanted highlighted within the quote if it was a word array, but having the color be unique for a single or couple index values of a string in Basil was difficult. I had not managed to figure it out.

An idea I had before the book of kindness:

scan-6

Physical book of kindness brainstorms:

scan scan-1

I had really wanted the design of the pages to be cognizant of the fact that as a physical book, it had depth and extra dimensions to play with. So while the hearts on each page look as if they are traveling bit by bit to be more fully on the page, the edge it leaves helps form a half heart on the side of the book – which helps form an entire heart when you look at the book from an angle.

Metaphorically, I like the aspect of a heart emerging the deeper into the book you are.

Maeda media was an inspiration! Thank you Golan for the recommendation!

Originally I really wanted the the flipbook heart feature to have more than just a moving heart, but in fact a question mark on the outside side edge of the book – as if the title “Book of kindness” or “Something Kind” ended in a “?” as it wrapped around the corner. The “?” would be lerped gradually into a exclamation mark that would end the book be reaffirming that kindness is an action!!! Exclamation! No more “?”.

 

Idea:

Enter a time (perhaps someone’s birthday, a time you are feeling really down, the date of someone’s death, etc)

Through Temboo – program runs through twitter to finds  365 tweets that have the word “hate” (or “sad” /”lonely”?) in it posted at that time.

It then sends a mechanical turk request for human computation to spurt out 365 examples of either—–

1. An unexpected act of kindness

Take a moment to remember something nice that someone did for you recently, that you weren’t expecting. In a single sentence or two, please describe what happened, and how it made you feel.

Here are a couple examples of what your sentence might look like:

  • One of my roommates cleaned up the fridge in our apartment.
  • I was running late and in a big hurry. A man let me cut in front of him in the line at Starbucks.

//3. A specific little detail of life that makes them smile or laugh

4. A link to a post, picture, quote, gif, video, etc that inspires, uplifts, makes them smile, etc (the program will take that link and generate a visual qr code to include)

Given this, the hate quote is printed really small in black and white on one side of a two page spread, and the happier result on the other side at the edge of a page in a lerping gradient color from the rainbow in relation to the page’s place in the book.

The height position of the happy quote is determined by the page’s position within the book in relation to an arc formula – that way when you flip through the book quickly, the flip book animation is that of a rainbow arching.

or like maeda @ media – the quotes printed at the edges creates a word when you see the book from the side – a sketchbook for somebody

The words/sentences are then processed by RITA to be ordered to rhyme

The VERBS are then processed by RITA and the typography is different to emphasis ACTIOOOONNN


PROCESS/what actually happened:

  1. Index.html Page request form  Wasn’t able to get automated external hit request to amazon to work – something about my AWS authentication key not working? So I ended up just manually submitting requests through the gui request maker amazon provides instead.
  2. External Hit Initialization (use Boto SDK to access Amazon Web Services API)   Tried Boto, AWS authentication key wrong, and just a lot of plumbing issues that didn’t necesarily have to do with art or code…and trying out a different SDK like mturk.js was not something I could figure out/learn quickly enough before the free book deadline. Ah…the joys of plumbing.
  3. Illustration generator see below
  4. Verb identifier & selector see below
  5. JSON Content file generator see below
  6. Basil.js script see below
  7. was on a flight – didn’t check email about extension I learned to bind books by hand!!! It was fun – if incredibly time consuming. My book binding process is below

screen-shot-2016-10-23-at-9-06-17-pm

Wrote a processing program to create the flipbook heart illustrations that go on each page by exporting each frame.heartpages

And then in Photoshop, I took all the pages and created an action series (1. reflect over the vertical axis, 2. save, 3. close file) and was able to automate the batch. All 53 illustrations were flipped horizontally in 3.256 seconds :). LOL….but it took a good chunk of time to figure it all out…../fix issues

hearts

 

Wrote an additional processing program that ran through all the quotes from amazon mechanical turk workers leveraging the RiTa library to identify and single out the verbs in the strings. All the verbs are subsequently gathered into an array – wherein I manually chose which verbs I wanted to include. This list of selected verbs is then used in the next program.

Wrote another processing program to create the json file of all the content each page of the book should have. It took the collection of strings from the csv doc of the turker results, the turker’s ID, a corresponding image file, and an array of all the highlighted verbs within the quote.

It generates differently each time (almost – the string data from the turkers stay consistent because I didn’t pay for more….)

See pdf:

bookofkindness

bookofkindness2

somethingkindagain

Flipping through the pages —  the heart translates closer and closer to the center as the book progresses, this allows the edge to create the heart shape on the side.

Here’s a video of Golan flipping through the book:


 

Hand bound the book by organizing the 112 pages into smaller booklets called ‘signatures’ of 4 large sheets printed double sided with spreads – which translated to 16 pages per pamphlet which were then sewn together.

scan-4img_1827

 

screen-shot-2016-10-28-at-2-37-06-am 7 different pdfs for the 7 different signatures needed

The wonkiness of the printing was actually not a computational error on my part, but the side effect of an act of kindness I received!

I had really loved the paper luster of the 80 lb, but my code wasn’t made for that (the measurements of the heart illustration and the placements weren’t adjusted for the widths of the 80 lb papers adding up, I had 50 lb in mind). Fedex only had 32lb and 80 lb. But the 32 lb paper didn’t feel nearly as good. So I had sighed, and grudgingly still went with the 32 lb – it would be narrower than I would’ve liked for the heart’s side edge, but would suffice. Especially since 80 lb printing would cost me $144.

But when I had picked up the order, the fedex guy said he printed it all out on 80 lb paper for me instead, and that he didn’t charge me the extra $70.

….DDISSSAAASSSSTTTEEER!!! HOW COULD I SAY NO?! I couldn’t just ask him to redo 112 pages of $144 printing!

I also thought it added to the meaning of the book – this book was made with the help of an act of kindness.
Here’s the heart generator code:

import processing.pdf.*;
boolean bRecordingPDF;
int pdfOutputCount = 0; 

int numofpg = 53;
int whatpg =0;
float r;
float g;
float b;
float rr;
float gg;
float bb;
float rdiff;
float gdiff;
float bdiff;
void setup(){
  bRecordingPDF = true;
  frameRate(5);
  size(116, 159);
  r = random(100,255);
  g = random(100,235);
  b = random(100,235);
  rr = 255;
  gg = 120;
  bb = 120;
  println("("+r+","+g+","+b+")");
}
void draw(){
  background(255);
    if (bRecordingPDF) {
      if(whatpg<=numofpg){
     beginRecord(PDF, "heart" + pdfOutputCount + ".pdf");
    translate(width-whatpg,height/2.4);
    
    
    noStroke();
    float red = map(whatpg,0,numofpg,r,rr);
    float green = map(whatpg,0,numofpg,g,gg);
    float blue = map(whatpg,0,numofpg,b,bb);
    fill(red,green,blue);
    PShape heart = createShape();
    heart.beginShape();
      for (int i=0; i<50; i++){
        float t = i*2*PI/50;
        float x = 3.3*(16*pow(sin(t),3));
        float y = -3.3*(13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t));
        heart.vertex(x,y);
      }
    heart.endShape();
    shape(heart, 0, 0);
    whatpg++;
  }
    endRecord();
    bRecordingPDF = false;
    pdfOutputCount++;
  }
}
void keyPressed() {
  // When you press a key, it will initiate a PDF export
  bRecordingPDF = true;
}

Here is the code that identifies the verbs!

 
import rita.*;
String[] kindverbs = {"hired","save","accept","asked","volunteering","make","made","makes","cursed","thanked","waiting","bought","shocked","given","ask","moved","paying","pray","surprised","helped","pay","offered","give","remember","remembering","gave","inspired","improve","paid","thank","help","making","enjoy","blessed","appreciated","cheered","touching","vouched","listening","cared","touched","wrote","promised","served","found","understood","understand","assured","strive"};
JSONArray[] highlight = new JSONArray[50];
String[] rawKindness = {
   "A coworker hired me for extra work, because she knew I was trying to save up money. At first I was hesitant to accept because she asked me before she asked her assistant, but it was okay with the assistant. I was grateful that she thought of me for the task, which was something I would have been happy volunteering to do.",
 "My neighbor stopped by my house and brought me a cinnamon broom. Those are those little straw brooms that you see in the stores in the fall that make the house smell really nice. It was so sweet of her and so unexpected! It made me very happy and it makes me happy every day when I see it.",
"I was in a hurry to get to the train station and cursed out a woman under my breath as I passed her.  Then, my wallet fell out of my pocket and onto the street.  She picked it up and handed it to me.  I thanked her sincerely and felt like a huge heel!",
"I was waiting on my friend to get back from doing something while in a Skype call with her.  We were about to play a game, however, she had told me to wait.  As soon as she got back, I had gotten an in-game notice stating she bought me an in-game item I have been wanting for quite some time, I was really shocked as she had already given me a couple other things prior to this so it was very unexpected and a huge surprise to me since I didn't ask for it.",
 "I was a single, 18 year old mother with my 1 year old daughter. I had just moved into my own place that was across the street from Walgreens. We walked together to grab some bread and applesauce. I was so embarrassed to be paying in change, and it was obvious the cashier was less than thrilled. As we were walking back across the street and go into the apartment complex a truck pulled up along side me. It was a husband and wife who told me I shouldn't be embarrassed and to be proud of how hard I was working. I was already in tears, and then they handed me a $20 bill, and that they'd pray for me. ",
 "This was over 10 years ago and I've never forgotten that moment. Still makes me tear up :)",
 "I was 7 months pregnant with my first child. My husband and I were visiting my parents and siblings who lived 9 hours away. I thought we were just picking up a crib my parents had bought us. Upon entering my parents house, we were surprised to see a bunch of close family and friends. It was a surprise baby shower for us! We were so happy to see everyone and receive so many, many gifts.",
 "I was in a very desperate financial situation and my best friend knew about it. So he knocked on my door and handed me $20. I was so, so thankful and grateful for that act, because I was able to buy food. I love him.",
 "I had a tire blow out on a rainy night while going off an off-ramp in the middle of nowhere.  A cop pulled over to see if I was alright, and let me borrow his umbrella.  He stayed there until I got my tire changed to make sure I was safe.",
 "My Mom recently made my husband and I sauerkraut with fresh rice (one of my favorite foods of hers). I was surprised and thrilled that she had made it for us - and also relieved, because we don't know how to cook that much nor have much money, and needed some healthy food desperately.",
 "My mother made me food when I was sick. She also helped me pay a bill to. It made me feel great and relived. It was a surprise.",
 "I stopped at a gas station last week because my car had ran out of gas. When I got to the station I realized I didn't have my wallet. Frantically looking around my car a nice woman asked if something was wrong to which I explained to her that I left my wallet. She offered to give me 5$ for gas to get on my way.",
 "I was at the grocery store just trying to buy a few things for dinner that night. Nothing real expensive, about $15-$20 worth of stuff. My card got declined. I had no other payment. I was going to leave the stuff there when the person in line behind me offered to just pay for it. I was really grateful and also super shocked because you don't see that kind of thing anymore.",
 "My car had broken down taking my kids to school. Another mother drove by and offered to take my kids to school. Then she was kind enough to return, picked me up and drove me home.",
 "I am usually the one to make sure the laundry is done for everyone in the house. For a few weeks I had been working really hard, trying to get in a lot of overtime, and in between working from home one evening I put a load of clothes in the washer. I completely forgot about them and before I fell into bed I remember the clothes. I rushed out to the washer to put the clothes in the dryer and saw my son had not only tried the clothes, but had folded them. I got tears in my eyes for how nice that felt.",
 "To be honest I had hard times remembering anything.. A couple weeks ago I was at the groceries store, an old man and I going to the cashier, he was in front of me: he picked the left line and I continued to the right. His line was going to be closed with the client before him so he moved to my line. I told him to pass in front of me and he replied he wasn't in a hurry because he was retired.",
 "One nice thing that someone did for me recently that I wasn't expecting was when my dad went to Trader Joe's and bought healthy food for me after I had been feeling unwell. He usually shops at a different grocery store , but he did it because he knew that it would be food that I would like.",
 "I was in the office before lunchtime somene gave me a candy, . It was super unexpected, I felt super good. I really I needed",
 "My sister surprised me by leaving fruit snacks at my front door the other day. I love fruit snacks. It was a small thing, but made my day.",
 "I was given a really nice compliment at work.  I was told I inspired someone to improve at their job and she got a promotion because of it.  It was incredibly heartwarming.",
 "I was in the drive-thru to pick up my starbucks for my office.  As I pulled up to the window, the barista told me that my order had been paid for by the car in front of me.  I was extremely grateful for the kindness and paid it forward when I could.",
 "It was late at night, and I had just gotten out of the gym. I walked to the parking lot, got in my car, and tried to start it, and the battery was dead. A fellow gym-goer came out shortly after, and I guess he heard me repeatedly try and start my car, which was unsuccessful. He walked up to my window and asked me if I needed a jump - and I said absolutely, yes, thank you! He pulled his car around, we got the batteries connected, and 30 seconds later I was on my way. What a life-saver! I was relieved to not have to waste money calling roadside assistance, and was quite frankly seriously positively surprised that someone would go out of their way to help me. It was very heartening, and I now make it a point to 'pay it forward' as far as these sorts of little kindnesses go.",
 "I was really tired and we were out of coffee. I had to go to a 7 hr online trainig class and didnt know how I'd make it through. My husband shows up with coffee for me, I was so happy and grateful.",
 "Our HR Manager was making cheese, egg and ham biscuits for everyone to enjoy one morning. I am a vegetarian and the ham was mixed in so I would not have been able to partake of the goodies. However she surprised me by making 4 separate ham  free biscuits just for me.  It was a very nice and welcome treat!",
 "My buddy let me sleep on his couch for a week.  I really needed a friend right then.  Felt great to have one pull through for me.",
 "I recently was cleaning my home and wanted to get rid of a lot of items around the house. I started early in the morning and was extremely surprised at how well the house was looking, for the first time in over a year the clutter was gone. But I noticed  I had lots of stuff out by the curb and wondering now if the city will pick it up.",
 "To my surprise my neighbor who has a truck  came over and loaded all the garbage on his truck and took it to the dump.",
 "I was so relieved that it was all gone. I felt so blessed to have such a nice neighbor.",
"A young lady offered me her seat on the subway.  Now, at 63 I'm not that old but I do have gray hair.  I appreciated her offer though I did not take her up on it - what a kind young lady.",
"It was surprising group kindness.  I went to a dance class tonight and the cirque class was doing dive cart wheels.  I thought I could do them and asked to join in.  The teacher let me.  I was the last one and the others cheered really loud when I did it.  Normally, we don't cheer in class, so it was a big surprise!  I'm almost 3 times older than them...I guess that might be the reason....but am not sure.",
"I had had a really bad day and a friend that I had been texting about stuff I had been going through was there for me. She gave up the rest of her afternoon at work and showed up at my apartment and took me to lunch and then we just sat and talked. Totally unexpected. Glad she gave me time.",
 "I had taken the day off work because I was very tired. My mom stopped by just to bring me a can of coke which she knows is my favorite drink. I felt like someone cares about me.",
"I had a fight with my boyfriend and was walking around our neighborhood which was in a downtown area. A homeless person bought me a beer and talked about angels and God. It was a really touching moment. I felt as though it was a message from above even if that sounds ridiculous.",
"I was in between jobs and really eager to get my foot in my career. I had posted an article on Facebook about the type of career I wanted, nothing too long and nothing like rant. A friend read it and messaged me right away, telling me she had a cousin who was in that field. Without me being able to reply, I got a text message from the cousin saying how my friend had vouched for me and if I would be available for an interview. Fast forward to now and I am loving my job! It was such a helpful extended act of kindness that changed my life.",
"My husband made the bed yesterday. I know it sounds trivial but it was a really nice surprise to walk in on. I hate making beds and I also hate sleeping in unmade beds so it meant a lot to me. It made me feel special and appreciated.",
"My dog was sick and I had just brought my favorite goat buck in for a second surgery. I was stressed out and upset because I have no other family than my animals. I was telling all this to a cashier at an organic grocer store I shop at. She an I have been friendly for over a year. After listening to me she said it hurt her to see me so sad and she asked me if she could give me a hug. I haven't had a hug from anyone in years so it made me feel like I was cared about by someone in a genuine way. I felt touched and sad at the same time, realizing just how long it had been since anyone showed me a tender act of care.",
"I was recently on the side of the road with a flat tire and a sweet boy stopped and changed my tire for me. I felt really thankful and also know there is still some good in this world.",
"A person who I didn't expect to show sympathy, actually was very upset when I told him about my mother's recent death - he even wrote a note.",
"I was out to dinner at Outback Steakhouse with my family (5 of us). My 3 young kids were being somewhat challenging, and I was a little stressed and not really enjoying my meal.  When we were done, the waiter came to us and said someone at the bar had paid for our meal, tip and everything. We immediately looked up, and no one was there. I was so in shock, confused, and happy....it really made my day.  I posted about it on Facebook and thanked the stranger who paid for our meal, and promised to pay it forward. I received a lot of positive feedback and comments from people.",
"My female cousin once unexpectedly baked a cake and served me a piece. I felt this was quite unusual since she's normally lazy and never does things like this (for anyone), although the inverse is not quite the same. I enjoyed it and found it unusual (the gesture), but coined it as a random act of kindness from her.",
"I was trying to buy a house and someone offered me the money I needed to put down on the house. I was over joyed and shocked that someone would help me in that.",
"I was in a video store (ok not extremely recently but it is what came to mind) and talking to my daughter about what we could get because I had rent due and was really short on money and it was just a bad time in general.  The next thing I knew a somewhat scruffy-looking guy came up and handed me a five dollar bill and kept walking.  I just stood there in shock for a little bit.  I was a little embarrassed, but is was such a nice thing for somebody to have done.",
"The grocery store where I shop regularly was crowded. I had other important work to do and was short on time. The helper at the grocery store understood my predicament  and helped me pay my grocery bill by opening a new counter. He really made my day!",
"One day, I happened to be in a town quite unfamiliar to me. The people there spoke a different language which I could not understand well. Similarly, they could not understand what I was telling them. I wanted to go to a hotel run by somebody who speaks my language. I knew only my mother tongue besides English. There gathered a number of taxi drivers  around me to take advantage of my miserable situation. Seeing the pathetic condition I was in, a young man stepped forward and asked what the matter was. He spoke in good English. I felt very much relieved and happy. I explained my situation and requested him to help. First he assured me that he was not a con and showed me a chain around his neck to prove that he was a devotee of one of the local Gods. He then called one of the taxi drivers and gave instructions as to where to take me. The taxi took me to a hotel whose owner spoke the same language as mine. I felt greatly grateful to the person who helped me in this difficult situation.",
"A man let me go in front of him at the grocery store when I only had a few items and he had a cart full.  I was very relieved because I had a rowdy toddler with me.  It was very nice of him and I was very appreciative.",
"I was on my way to one of my hardest midterms last year, physics. Unexpectedly, a girl was handing out packets containing snacks and other various test supplies out on campus, which was a really nice.",
"A man I had just met, changed the flat tube in my tire five minutes before the mountain bike race that we were both competing in. He did this without hesitation. Amazing.",
"I just did some groceries. I got some extra snacks and drinks. I saw a homeless senior citizen resting on a bench, and decided on a whim to share my snacks and drinks.",
"I was having a really bad day out of nowhere. My mom showed up to my apartment and brought flowers. She does not even live in the same town as me. It was very thoughtful of her. I felt grateful for her.",
"I always strive for a clean house but rarely get it. Not only did I walk into a clean house after work on my Birthday, but my man also got me the most beautiful flowers! I always want nothing but flowers for my day and this time he came through and then some! It was awesome and made me feel good.",
};
JSONArray values;
void setup(){
  values = new JSONArray();
  for(int i=0;i<rawKindness.length;i++){//go through the entire array of quotes to get to each quote
    String[] quote = RiTa.tokenize(rawKindness[i]);//make each quote into an array of words
    
    for(int j=0; j<quote.length; j++){//run through each word in the quote
      for(int k=0;k<kindverbs.length;k++){//test to see if it matches any of the desired verbs
        String quotedword = quote[j];
        String kindword = kindverbs[k];
        if(quotedword.equals(kindword)){
          //save the index values of the matching words within the quote
        }
       
      //add those index values into an array that gets out into the JSONarray of arrays
      }
    }
  }
}

 


JSON outputter:

 
import rita.*;
String[] images = new String[50];
String[] imageAgain = new String[50];
String[] id = {"A14OO6SSSYQQBL","A17LIM50IMQN7Y","A1CE2XPYCDRHVZ","A1EG66WQGRUPCS","A1GKTC682SWY23","A1L2T6S3WLN692","A1N91AUYC0H7S4","A1NKBXOTZAI1YK","A1O67YS3DU0ZHX","A1P1UEH9PMSHXG","A2JR1GWH41MLRB","A2LEY4X3LD8G26","A2M4YIC3XU2693","A2O2Q0LVWOWJ1G","A2VRDE2FHCBMF8","A2ZE2IPHK152KA","A30QV5VTBRGYTA","A31681CCEVDIH3","A38TZK07ON180S","A3NS1DN6J7Z3EU","AG5X553DRP557","AI4LKMF5QD07D","AITY0PTUTW9HW","AMPMTF5IAAMK8","AW5O1RK3W60FC","A2W7Z10Z5YLLF4","A3A8P4UR9A0DWQ","A2FBJV1VAI9SQ6","A2S3BJYB8MXB9A","A31LK6KUPJHPSP","A2L5GOTR5Y42X1","A1U6A617Q95MTZ","A1FFPO2ZR40NCD","A1MJYGGQ9RQUKX","A3CS3BQE8T1BU0","A3S0INU046UTYK","A2P6UJ9BKRPGUS","A03922113RU44GENR8ATX","A1RSAU5I9NZTOP","A27IUQDZPHETTP","A2A5YQTRSCLO32","A1UJFULCCBBIJI","A3JMJIKX36UNC7","A28JGET2XTPRG5","A2YRYS17DU3A8R","A17X4M2GVN70VB","AX0KHOWMMM6Z2","A10PVSMU1UBP8N","A116GKYM2O195O","A2DAZ0CJ3VKS80"};
//this is the array of arrays of indexvalues of indivudal verbs in each quote that need to change color
JSONArray[] highlightedIndices = new JSONArray[50];
String[] kindverbs = {"hired","save","accept","asked","volunteering","make","made","makes","cursed","thanked","waiting","bought","shocked","given","ask","moved","paying","pray","surprised","helped","pay","offered","give","remember","remembering","gave","inspired","improve","paid","thank","help","making","enjoy","blessed","appreciated","cheered","touching","vouched","listening","cared","touched","wrote","promised","served","found","understood","understand","assured","strive"};
String[] rawKindness = {
   "A coworker hired me for extra work, because she knew I was trying to save up money. At first I was hesitant to accept because she asked me before she asked her assistant, but it was okay with the assistant. I was grateful that she thought of me for the task, which was something I would have been happy volunteering to do.",
 "My neighbor stopped by my house and brought me a cinnamon broom. Those are those little straw brooms that you see in the stores in the fall that make the house smell really nice. It was so sweet of her and so unexpected! It made me very happy and it makes me happy every day when I see it.",
"I was in a hurry to get to the train station and cursed out a woman under my breath as I passed her.  Then, my wallet fell out of my pocket and onto the street.  She picked it up and handed it to me.  I thanked her sincerely and felt like a huge heel!",
"I was waiting on my friend to get back from doing something while in a Skype call with her.  We were about to play a game, however, she had told me to wait.  As soon as she got back, I had gotten an in-game notice stating she bought me an in-game item I have been wanting for quite some time, I was really shocked as she had already given me a couple other things prior to this so it was very unexpected and a huge surprise to me since I didn't ask for it.",
 "I was a single, 18 year old mother with my 1 year old daughter. I had just moved into my own place that was across the street from Walgreens. We walked together to grab some bread and applesauce. I was so embarrassed to be paying in change, and it was obvious the cashier was less than thrilled. As we were walking back across the street and go into the apartment complex a truck pulled up along side me. It was a husband and wife who told me I shouldn't be embarrassed and to be proud of how hard I was working. I was already in tears, and then they handed me a $20 bill, and that they'd pray for me. ",
 "This was over 10 years ago and I've never forgotten that moment. Still makes me tear up :)",
 "I was 7 months pregnant with my first child. My husband and I were visiting my parents and siblings who lived 9 hours away. I thought we were just picking up a crib my parents had bought us. Upon entering my parents house, we were surprised to see a bunch of close family and friends. It was a surprise baby shower for us! We were so happy to see everyone and receive so many, many gifts.",
 "I was in a very desperate financial situation and my best friend knew about it. So he knocked on my door and handed me $20. I was so, so thankful and grateful for that act, because I was able to buy food. I love him.",
 "I had a tire blow out on a rainy night while going off an off-ramp in the middle of nowhere.  A cop pulled over to see if I was alright, and let me borrow his umbrella.  He stayed there until I got my tire changed to make sure I was safe.",
 "My Mom recently made my husband and I sauerkraut with fresh rice (one of my favorite foods of hers). I was surprised and thrilled that she had made it for us - and also relieved, because we don't know how to cook that much nor have much money, and needed some healthy food desperately.",
 "My mother made me food when I was sick. She also helped me pay a bill to. It made me feel great and relived. It was a surprise.",
 "I stopped at a gas station last week because my car had ran out of gas. When I got to the station I realized I didn't have my wallet. Frantically looking around my car a nice woman asked if something was wrong to which I explained to her that I left my wallet. She offered to give me 5$ for gas to get on my way.",
 "I was at the grocery store just trying to buy a few things for dinner that night. Nothing real expensive, about $15-$20 worth of stuff. My card got declined. I had no other payment. I was going to leave the stuff there when the person in line behind me offered to just pay for it. I was really grateful and also super shocked because you don't see that kind of thing anymore.",
 "My car had broken down taking my kids to school. Another mother drove by and offered to take my kids to school. Then she was kind enough to return, picked me up and drove me home.",
 "I am usually the one to make sure the laundry is done for everyone in the house. For a few weeks I had been working really hard, trying to get in a lot of overtime, and in between working from home one evening I put a load of clothes in the washer. I completely forgot about them and before I fell into bed I remember the clothes. I rushed out to the washer to put the clothes in the dryer and saw my son had not only tried the clothes, but had folded them. I got tears in my eyes for how nice that felt.",
 "To be honest I had hard times remembering anything.. A couple weeks ago I was at the groceries store, an old man and I going to the cashier, he was in front of me: he picked the left line and I continued to the right. His line was going to be closed with the client before him so he moved to my line. I told him to pass in front of me and he replied he wasn't in a hurry because he was retired.",
 "One nice thing that someone did for me recently that I wasn't expecting was when my dad went to Trader Joe's and bought healthy food for me after I had been feeling unwell. He usually shops at a different grocery store , but he did it because he knew that it would be food that I would like.",
 "I was in the office before lunchtime somene gave me a candy, . It was super unexpected, I felt super good. I really I needed",
 "My sister surprised me by leaving fruit snacks at my front door the other day. I love fruit snacks. It was a small thing, but made my day.",
 "I was given a really nice compliment at work.  I was told I inspired someone to improve at their job and she got a promotion because of it.  It was incredibly heartwarming.",
 "I was in the drive-thru to pick up my starbucks for my office.  As I pulled up to the window, the barista told me that my order had been paid for by the car in front of me.  I was extremely grateful for the kindness and paid it forward when I could.",
 "It was late at night, and I had just gotten out of the gym. I walked to the parking lot, got in my car, and tried to start it, and the battery was dead. A fellow gym-goer came out shortly after, and I guess he heard me repeatedly try and start my car, which was unsuccessful. He walked up to my window and asked me if I needed a jump - and I said absolutely, yes, thank you! He pulled his car around, we got the batteries connected, and 30 seconds later I was on my way. What a life-saver! I was relieved to not have to waste money calling roadside assistance, and was quite frankly seriously positively surprised that someone would go out of their way to help me. It was very heartening, and I now make it a point to 'pay it forward' as far as these sorts of little kindnesses go.",
 "I was really tired and we were out of coffee. I had to go to a 7 hr online trainig class and didnt know how I'd make it through. My husband shows up with coffee for me, I was so happy and grateful.",
 "Our HR Manager was making cheese, egg and ham biscuits for everyone to enjoy one morning. I am a vegetarian and the ham was mixed in so I would not have been able to partake of the goodies. However she surprised me by making 4 separate ham  free biscuits just for me.  It was a very nice and welcome treat!",
 "My buddy let me sleep on his couch for a week.  I really needed a friend right then.  Felt great to have one pull through for me.",
 "I recently was cleaning my home and wanted to get rid of a lot of items around the house. I started early in the morning and was extremely surprised at how well the house was looking, for the first time in over a year the clutter was gone. But I noticed  I had lots of stuff out by the curb and wondering now if the city will pick it up.",
 "To my surprise my neighbor who has a truck  came over and loaded all the garbage on his truck and took it to the dump.",
 "I was so relieved that it was all gone. I felt so blessed to have such a nice neighbor.",
"A young lady offered me her seat on the subway.  Now, at 63 I'm not that old but I do have gray hair.  I appreciated her offer though I did not take her up on it - what a kind young lady.",
"It was surprising group kindness.  I went to a dance class tonight and the cirque class was doing dive cart wheels.  I thought I could do them and asked to join in.  The teacher let me.  I was the last one and the others cheered really loud when I did it.  Normally, we don't cheer in class, so it was a big surprise!  I'm almost 3 times older than them...I guess that might be the reason....but am not sure.",
"I had had a really bad day and a friend that I had been texting about stuff I had been going through was there for me. She gave up the rest of her afternoon at work and showed up at my apartment and took me to lunch and then we just sat and talked. Totally unexpected. Glad she gave me time.",
 "I had taken the day off work because I was very tired. My mom stopped by just to bring me a can of coke which she knows is my favorite drink. I felt like someone cares about me.",
"I had a fight with my boyfriend and was walking around our neighborhood which was in a downtown area. A homeless person bought me a beer and talked about angels and God. It was a really touching moment. I felt as though it was a message from above even if that sounds ridiculous.",
"I was in between jobs and really eager to get my foot in my career. I had posted an article on Facebook about the type of career I wanted, nothing too long and nothing like rant. A friend read it and messaged me right away, telling me she had a cousin who was in that field. Without me being able to reply, I got a text message from the cousin saying how my friend had vouched for me and if I would be available for an interview. Fast forward to now and I am loving my job! It was such a helpful extended act of kindness that changed my life.",
"My husband made the bed yesterday. I know it sounds trivial but it was a really nice surprise to walk in on. I hate making beds and I also hate sleeping in unmade beds so it meant a lot to me. It made me feel special and appreciated.",
"My dog was sick and I had just brought my favorite goat buck in for a second surgery. I was stressed out and upset because I have no other family than my animals. I was telling all this to a cashier at an organic grocer store I shop at. She an I have been friendly for over a year. After listening to me she said it hurt her to see me so sad and she asked me if she could give me a hug. I haven't had a hug from anyone in years so it made me feel like I was cared about by someone in a genuine way. I felt touched and sad at the same time, realizing just how long it had been since anyone showed me a tender act of care.",
"I was recently on the side of the road with a flat tire and a sweet boy stopped and changed my tire for me. I felt really thankful and also know there is still some good in this world.",
"A person who I didn't expect to show sympathy, actually was very upset when I told him about my mother's recent death - he even wrote a note.",
"I was out to dinner at Outback Steakhouse with my family (5 of us). My 3 young kids were being somewhat challenging, and I was a little stressed and not really enjoying my meal.  When we were done, the waiter came to us and said someone at the bar had paid for our meal, tip and everything. We immediately looked up, and no one was there. I was so in shock, confused, and happy....it really made my day.  I posted about it on Facebook and thanked the stranger who paid for our meal, and promised to pay it forward. I received a lot of positive feedback and comments from people.",
"My female cousin once unexpectedly baked a cake and served me a piece. I felt this was quite unusual since she's normally lazy and never does things like this (for anyone), although the inverse is not quite the same. I enjoyed it and found it unusual (the gesture), but coined it as a random act of kindness from her.",
"I was trying to buy a house and someone offered me the money I needed to put down on the house. I was over joyed and shocked that someone would help me in that.",
"I was in a video store (ok not extremely recently but it is what came to mind) and talking to my daughter about what we could get because I had rent due and was really short on money and it was just a bad time in general.  The next thing I knew a somewhat scruffy-looking guy came up and handed me a five dollar bill and kept walking.  I just stood there in shock for a little bit.  I was a little embarrassed, but is was such a nice thing for somebody to have done.",
"The grocery store where I shop regularly was crowded. I had other important work to do and was short on time. The helper at the grocery store understood my predicament  and helped me pay my grocery bill by opening a new counter. He really made my day!",
"One day, I happened to be in a town quite unfamiliar to me. The people there spoke a different language which I could not understand well. Similarly, they could not understand what I was telling them. I wanted to go to a hotel run by somebody who speaks my language. I knew only my mother tongue besides English. There gathered a number of taxi drivers  around me to take advantage of my miserable situation. Seeing the pathetic condition I was in, a young man stepped forward and asked what the matter was. He spoke in good English. I felt very much relieved and happy. I explained my situation and requested him to help. First he assured me that he was not a con and showed me a chain around his neck to prove that he was a devotee of one of the local Gods. He then called one of the taxi drivers and gave instructions as to where to take me. The taxi took me to a hotel whose owner spoke the same language as mine. I felt greatly grateful to the person who helped me in this difficult situation.",
"A man let me go in front of him at the grocery store when I only had a few items and he had a cart full.  I was very relieved because I had a rowdy toddler with me.  It was very nice of him and I was very appreciative.",
"I was on my way to one of my hardest midterms last year, physics. Unexpectedly, a girl was handing out packets containing snacks and other various test supplies out on campus, which was a really nice.",
"A man I had just met, changed the flat tube in my tire five minutes before the mountain bike race that we were both competing in. He did this without hesitation. Amazing.",
"I just did some groceries. I got some extra snacks and drinks. I saw a homeless senior citizen resting on a bench, and decided on a whim to share my snacks and drinks.",
"I was having a really bad day out of nowhere. My mom showed up to my apartment and brought flowers. She does not even live in the same town as me. It was very thoughtful of her. I felt grateful for her.",
"I always strive for a clean house but rarely get it. Not only did I walk into a clean house after work on my Birthday, but my man also got me the most beautiful flowers! I always want nothing but flowers for my day and this time he came through and then some! It was awesome and made me feel good.",
};
JSONArray values;
void setup(){
  values = new JSONArray();

  for (int i = 0; i < rawKindness.length; i++) {
    JSONArray indicesArray = new JSONArray();
    StringList tempIndexValue = new StringList();
    String[] quote = RiTa.tokenize(rawKindness[i]);//make each quote into an array of words
    
    for(int j=0; j<quote.length; j++){//run through each word in the quote to test for verb matches
      for(int k=0;k<kindverbs.length;k++){//test to see if it matches any of the desired verbs
        String quotedword = quote[j];
        String kindword = kindverbs[k];
        if(quotedword.equals(kindword)){//test to see if it matches any of the desired verbs
          tempIndexValue.append(kindword);//add those index values into a list
        }
      }
    }
    for(int j=0;j<tempIndexValue.size();j++){//the list gets added into a json array
      String indexvalue = tempIndexValue.get(j);
      indicesArray.setString(j,indexvalue);
    }
    String filename = "heart"+i+"copy"+".jpeg";
    images[i] = filename;
    filename = "heart"+i+".jpeg";
    imageAgain[i] = filename;
    JSONObject kindness = new JSONObject();

    kindness.setInt("pg", i+2);
    kindness.setString("kindness", "\""+rawKindness[i]+"\"");
    kindness.setString("image", images[i]);
    kindness.setString("image2", imageAgain[i]);
    kindness.setString("author", id[i]);
    kindness.setJSONArray("indicesToHighlight", indicesArray);

    values.setJSONObject(i, kindness);
  }

  saveJSONArray(values, "data/book_content.json");
}

Here’s the Basil.js!

 
#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
// Load a data file containing your book's content. This is expected
// to be located in the "data" folder adjacent to your .indd and .jsx. 
var jsonString = b.loadString("book_content.json");
var jsonData;

//--------------------------------------------------------
function setup() {
  b.canvasMode(b.BLEED);
  // Clear the document at the very start. 
  b.clear (b.doc());

  // Make a Title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Taz","UltraLight"); 
  b.textAlign(Justification.LEFT_ALIGN); 
  b.text("SOMETHING KIND", 72,72,360,36);
  b.noStroke();  // no border around img
  var coverimg = b.image("images/heartCover.jpg",b.width-50,b.height*0.8039,50,b.height-b.height*0.8039);
  coverimg.fit(FitOptions.PROPORTIONALLY);

  
  // Parse the JSON file into the jsonData array
  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);

  b.addPage();
  //b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
  //b.text("Much thanks to Professor Golan Levin for the many all nighters you pulled with us",b.width*0.2,b.height*0.2, b.wdith*0.6, b.height*0.4);

  // Initialize some variables for element placement positions.
  // TIP: units are "points", 72 points = 1 inch.
  var quoteW = b.width/2;
  var quoteH = b.height/1.5;
  var quoteX = b.width/2-quoteW/2; 
  var quoteY = b.height/2-quoteH/2;

  var captionX = 0.2*b.width; 
  var captionY = b.height*0.7058;
  var captionW = b.width*0.6;
  var captionH = 36;

  var imageX = 0; 
  var imageXx = 496/612*b.width;
  var imageY = 453/612*b.height;

   b.println("dedication page");
  // Loop over every element of the book content array
  for (var i = 0; i < jsonData.length; i++) {
    b.println(i);
    // Create the next page. 
    b.addPage();

    // Load an image from the "images" folder inside the data folder;
    var anImageFilename = "images/" + jsonData[i].image;
    var anotherImageFilename= "images/" + jsonData[i].image2;
    /*if(i!=jsonData.length-1){
      anotherImageFilename = "images/" + jsonData[i+1].image2;
    }
    else anotherImageFilename = "images/" + jsonData[i].image2;*/
    var anImage = b.image(anImageFilename, 0, b.height*0.8039, 50, b.height-b.height*0.8039);
    anImage.fit(FitOptions.PROPORTIONALLY);

    b.fill(0);
    b.textSize(16);
    b.textKerning(22);
    b.textFont("Taz","UltraLight"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text(jsonData[i].kindness, quoteX,quoteY,quoteW,quoteH);

    // Create textframes for the "caption" fields
    b.fill(0);
    b.textSize(12);
    b.textFont("Taz","UltraLight"); 
    b.textAlign(Justification.RIGHT_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text("- "+jsonData[i].author, captionX,captionY,captionW,captionH);

    b.addPage();

    var anImageAgain = b.image(anotherImageFilename,b.width-50,b.height*0.8039,50,b.height-b.height*0.8039);
    anImageAgain.fit(FitOptions.PROPORTIONALLY);

    for(var j=0;j<jsonData[i].indicesToHighlight.length;j++){
      b.println(i+"verb"+j+" "+jsonData[i].indicesToHighlight[j]);
      var fillcolor = b.lerp(200, 50, b.pageCount()/104);
      b.fill(b.random(255,b.pageCount()),b.random(fillcolor+55,fillcolor),b.random(fillcolor+55,fillcolor));
      var tsizing = b.lerp(72,24,b.pageCount()/104);
      b.textFont("Taz","SemiLight"); 
      b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.CENTER_ALIGN );
      b.textSize(b.random(tsizing+20, tsizing-10));
      var tcoordinatey = b.random(b.height*0.1, b.height*0.9);
      var tcoordinatex = (b.random(0,b.width*0.7));
      b.text(jsonData[i].indicesToHighlight[j],tcoordinatex,tcoordinatey, b.width-tcoordinatex, 72);
    }
    

  };

  b.addPage();
  var anotherHeart = b.image("images/heart53copy.jpeg", 0, 526, 50, b.height-b.height*0.8039);
  anotherHeart.fit(FitOptions.PROPORTIONALLY);

  ///////GO & DO PAGE------------------------------
  b.addPage();
  var lastHeart = b.image("images/heart53.jpeg", 526,b.height*0.8039,50,b.height-b.height*0.8039);
  lastHeart.fit(FitOptions.PROPORTIONALLY);
  b.textSize(36);
  b.textFont("Taz","SemiLight"); 
  b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN);
  b.fill(255);
    b.text("DO", 533,528.25,72,72);
  b.fill(0);
    b.text("GO", 452,528.25,72,72);
  b.textSize(34);
  b.textFont("DIN","Regular"); 
  b.text("&", 501,524.5,72,72);

  //last words----------------------
  b.addPage();
  var lastImage = b.image("images/TurkThank.png", 0.2*b.width, 0.2*b.height, 0.6*b.width, 0.4*b.height);
  lastImage.fit(FitOptions.PROPORTIONALLY);
  b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
  b.textSize(10);
  b.text("This book is a computationally compilated and formatted collection of Mechanical Turk Worker responses. Above is an email I recieved from a turker",
    captionX,captionY,captionW,2*captionH);

}

// This makes it all happen:
b.go(); 
Written by Comments Off on Lumar-BookOfKindness Posted in Book

Deliverables 06 (D. 10/28)

This set of deliverables has three components, due at the beginning of class on Friday October 28th.

  • Looking Outwards #06: Bots
  • Viewing & Prep (for the Book)
  • A Computationally Generated Book

Looking Outwards #06: Bots

A bot is software that automatically generates cultural content and publishes this content online, generally in a way that others can subscribe to.

In this, your sixth Looking Outwards assignment, you are asked to identify a bot (probably on Twitter, but potentially elsewhere) which interests you. Write a brief Looking Outwards post about it. In your post, please include screenshots of two or three postings from the bot that you think are particularly good.

As usual, please categorize your post with the LookingOutwards06 Category. This makes your posts much easier to find (and grade). Title your post nickname-lookingoutward06.

Some places to find bots include:


Viewing & Prep

You are asked to view or review the following:

Allison Parrish: Exploring (Semantic) Space With (Literal) Robots:


A Computationally Generated Book

Assignment Statement

Write a program (or more likely: a set of programs) which generates a book. Your program(s) should generate the content of the book, as well as execute its layout automatically. Your book should have at least 20 pages, and must be printed and bound.

Learning Objectives

The purposes of this assignment are to prompt students toward:

  • Further application of generative principles: to generating text, images, layouts, and their comprehensive combination in a complex yet familiar physical object, a book.
  • Experience combining multiple self-written programs into a multi-stage workflow.
  • Exposure to a scripting language (Basil.js) for controlling a powerful commercial software application (Adobe InDesign) that would ordinarily be used ‘by hand’.
  • Exposure to a toolkit (RiTa) for language analysis and synthesis, and text analysis/synthesis concepts such as Markov chains and parts-of-speech taggers.
  • Awareness of generative text strategies, in the context of artists’ books.

Recommended Tools

It’s likely, though not necessarily guaranteed, that you will use Basil.js for this project. You might also find yourself using RiTa and Temboo.

  • Basil.js is a scripting library for Adobe InDesign, created by Ted Davis, Benedikt Groß and Ludwig Zeller et al. at the Basel School of Design in Switzerland.
  • RiTa is a set of software tools for computational literature (for Processing, Node, and p5.js) created by Daniel Howe.
  • Temboo is a set of libraries that allow you to access a very wide range of APIs, such as Twitter, and much, much more. They provide example code for 10 different languages (including JavaScript, Python, and Processing). Temboo is a powerful commercial product, but essentially free for students to tinker with.

Summary of Deliverables

This checklist won’t surprise you, but it’s nice to be reminded of what’s expected.

  • Consider books around you.
  • Develop a program (or more likely: a set of programs) to generate a book. See the section below, Details and Considerations, for more information.
  • Print and bind your book. Have this physical copy ready for the beginning of class on Friday, October 28.
  • Write a 100-word description of your book. Publish this description in a blog post on this web site. Title your blog post, nickname-book, and give your blog post the WordPress Category, Book.
  • Write an additional narrative of 150-200 words describing your development process, and evaluating your results. Please include this in your blog post, too. Include some information about your inspirations, if any.
  • Upload a photograph of your printed, bound book.
  • Upload a video or (preferably) an animated GIF of a video of you flipping through your book’s pages.
  • Upload a PDF of your book.
  • Upload some photos or scans of your notebook sketches.
  • Embed your code (using the WP-Syntax WordPress plugin to format your JavaScript and/or other code), and include a link to your code on Github. Be careful not to include your user keys or passwords to Temboo!!

Details and Considerations

The content is up to you. Take a look at books around you. How many kinds of books can you think of? Your book may have any combination of text, images, and/or graphics. Examples of possible books include, but are not limited to, things like:

  • A children’s alphabet book
  • A nonsense dictionary
  • A visual encyclopedia
  • An atlas, full of maps
  • A coloring book
  • A screenplay or script for a play
  • A phonebook for an imaginary location
  • A collection of letters between two lovers
  • A 365-day calendar with a horoscope-a-day
  • A “how-to” book, full of instructions or recipes
  • A field guide, describing the flowers/insects of a region
  • A collection of illustrated short stories, poems or sonnets
  • An illustrated catalogue depicting and describing objects for sale
  • A book of facts, representing a (daily) snapshot of data from around the world

Write a program (or more likely: a set of programs) to create a book. Your program(s) should generate the content of the book as well as its layout. Try and keep these two programs as cleanly separated as possible.

You will very likely need to write more than one program. For example:

  1. You might write a first program in Python or JavaScript which generates some poems, and saves those poems in JSON format.
  2. You might then write a second program in Processing to illustrate those poems, and save out these illustrations as PNG files.
  3. You might then write a third program in JavaScript to lay out the book containing these texts and images in Adobe InDesign using the Basil.js library.
  4. Note that it is almost certainly not advisable to attempt to do all of this (i.e. poem generation, illustration generation, book layout) using Basil.js! Bad idea. Instead, generate your content using one or more programs, and use Basil just for layout.

Working with Basil.js (SAMPLE CODE, YO)

Most of the complexity in working with Basil.js comes when trying to get your environment set up correctly. If you follow the instructions here VERY CAREFULLY, and I do mean very carefully, you should be alright. In addition to the many fun Basil.js demos that come with the download, I’ve prepared the following readymade example project for you (an illustrated Alphabet Book), which should help you get your own generative book started. It includes a template InDesign file (.indd), Basil.js code (.jsx), all necessary data (.json, .jpg), and a sample PDF output.

Our guest Marius Watz also has created a set of Basil examples for you, including a newly-revised example that lays out a book of Tweets with text frames that size themselves dynamically according to the length of your text. You can find his samples at:

More Considerations

Do some investigation. Check out tools like RiTa for generating rhymes. Check out tools like Temboo for accessing information from online sources, such as tweets or headlines.

Your book should have at least 20 pages. If there is a good reason why your book’s concept calls for fewer pages than this, please consult the professor for permission to make an exception.

Give consideration to the layout of your book. This includes things like your margins, typeface selection, etc., as well as your cover design. You may wish to read Chapter 8 (“Shaping the Page”, pp.143-178) of Robert Bringhurst’s classic Elements of Typographic Style.

Your book may not be laid out ‘by hand’. You are asked to generate your book by scripting Adobe InDesign using Basil.js. (Keep in mind that the units are likely to be points, or 1/72 of an inch.) However, if you are deeply screwed and Basil does not behave, you are permitted to generate a multi-page PDF in Processing. If you do this, you’ll probably have to sacrifice the quality of your typography.

Where Can I Print My Book?

Your book must be printed and bound. Here are five options:

perfect-bound-vs-saddle-stitched-1

Formats from Blurb.com

blurb2

blurb1

Formats from Lulu.com

lulu1

lulu2

A special printing offer. Using funds from our course budget, books completed by 12:00 Noon on Saturday October 22 will be printed and express-shipped from Blurb.com and/or Lulu.com. Books must cost less than $50 to print, not including shipping, and must be uploaded to Blurb or Lulu by Saturday 10/22, without exception. Students who miss the 10/22 deadline must pay for the printing of their own book. 

The Espresso Book Machine (at the U. Pitt University Store, 4000 Fifth Avenue):

ebm_web


Additional (Optional) Viewing

The following video lecture presentations are recommended optional viewing for those interested in this unit.

Darius Kazemi: How to Coax Soul from a Machine:

Lynn Cherny: Things I Think Are Awesome:

Nick Montfort: “#!Talk At Google

 

Comments Off on Deliverables 06 (D. 10/28) Posted in