Sunday, December 31, 2017
=== update
I updated my old Triple Equals Overrated ramble with a long postscript... while I'm not suggesting changing === as default for my company, or even the received wisdom of "avoid type coercion for comparisons), the people who like double equals aren't JUST dunderheaded cavepeople...
Thursday, December 28, 2017
coulda shoulda woulda for palm fans...
Palm's WebOS was the ultimate mighta-been of the early-ish smartphone era - that link explores how iPhone X is still cribbing from it.
Monday, December 25, 2017
pretty print json from unix shell
I've been digging using little .json files on the filesystem as a kind of DIY NoSQL database, but they're not always pretty to look at. Was looking for a pretty printer for 'em, thought I might have to do something in node or python but "json_pp" is already there in my Mac Terminal. The only weird thing is it only seems to read from STDIN (not from a file) so you have to do
json_pp < SOMEFILE.json
json_pp < SOMEFILE.json
Sunday, December 24, 2017
asssteroidsss and sound generation
I used to make small games fairly regularly, often for Glorious Trainwrecks. Sadly this might be my first (and last, I suppose!) game of the year...
Pippin Barr (who has a great blog) made a set of games called Sibilant Snakelikes, older games as seen through a classic Snake game lens (his take on Shadow of the Colossus is especially great)
Since I was in middle school, I've used the idea of "move something on screen with an x/y position by only adjusting an xspeed and yspeed variable and adding them to x and y position every clock tick" - it's a very easy to program way to get a pleasing sense of motion in a game world. (The pinnacle of exploiting this was probably my Atari 2600 mashup JoustPong)
Atari's Asteroids used a roughly similar physics, albeit with more sophisticated rotations, so I wanted to see what happens if I further shoehorned it into the quantized world of Snake -
You can see the game and fiddle with its code here. One thing it's doing is using the P5.js sound libraries to make the notes - I really like this approach, having all the graphics and the sounds embedded in one file as text is a great throwback to the old 8-bit type bit computer magazines, vs having external graphics and sound files. Unfortunately, the library isn't super clearly documented, and mixed up with the functions that rely on external mp3s or wavs... If my understanding firms up I'll try to write more on it. (I'm tempted to make my own mini-sequencer from it...)
Pippin Barr (who has a great blog) made a set of games called Sibilant Snakelikes, older games as seen through a classic Snake game lens (his take on Shadow of the Colossus is especially great)
Since I was in middle school, I've used the idea of "move something on screen with an x/y position by only adjusting an xspeed and yspeed variable and adding them to x and y position every clock tick" - it's a very easy to program way to get a pleasing sense of motion in a game world. (The pinnacle of exploiting this was probably my Atari 2600 mashup JoustPong)
Atari's Asteroids used a roughly similar physics, albeit with more sophisticated rotations, so I wanted to see what happens if I further shoehorned it into the quantized world of Snake -
You can see the game and fiddle with its code here. One thing it's doing is using the P5.js sound libraries to make the notes - I really like this approach, having all the graphics and the sounds embedded in one file as text is a great throwback to the old 8-bit type bit computer magazines, vs having external graphics and sound files. Unfortunately, the library isn't super clearly documented, and mixed up with the functions that rely on external mp3s or wavs... If my understanding firms up I'll try to write more on it. (I'm tempted to make my own mini-sequencer from it...)
Thursday, December 21, 2017
php - sending mail/email
I've found it tough to reliably send mail from PHP, and the command line in general.
It feels like gmail (my usual testing recipient) really hates old school ways of sending email via scripts - like to the extent stuff doesn't even wind up in the recipient's SPAM box, it just goes away.
I'll still look for a better solution but right now the most reliable thing I've found is
2019 UPDATE: I feel like at some point I discovered an "oh wait, is it that easy?" method for this which I then forgot... now I'm wondering if the linux command line "mailx" (vs the older "mail") might be the key that Google lets through (though don't try to forge the "from" very much!). And in fact the above PEAR method might not work with google as the smtp host.
It feels like gmail (my usual testing recipient) really hates old school ways of sending email via scripts - like to the extent stuff doesn't even wind up in the recipient's SPAM box, it just goes away.
I'll still look for a better solution but right now the most reliable thing I've found is
<?php
// Pear Mail Library
require_once "Mail.php";
$from = '<kirkjerk@gmail.com>';
$to = '<kirkjerk@gmail.com>';
$subject = 'Hi testing mail!';
$body = "Hi,\n\nHow are you, how are thigns going?";
$headers = array(
'From' => $from,
'To' => $to,
'Subject' => $subject
);
$smtp = Mail::factory('smtp', array(
'host' => 'ssl://smtp.gmail.com',
'port' => '465',
'auth' => true,
'username' => 'kirkjerk@gmail.com',
'password' => 'MYPASSWORD'
));
$mail = $smtp->send($to, $headers, $body);
if (PEAR::isError($mail)) {
echo('<p>' . $mail->getMessage() . '</p>');
} else {
echo('<p>Message successfully sent!</p>');
}
?>
As far as I can tell, much of the header stuff (like trying to munge the From) is ignored- plus you have to have your password in plain text in a script which stinks. I'll try to update this entry if I refine this technique, like getting reply-tos working etc.2019 UPDATE: I feel like at some point I discovered an "oh wait, is it that easy?" method for this which I then forgot... now I'm wondering if the linux command line "mailx" (vs the older "mail") might be the key that Google lets through (though don't try to forge the "from" very much!). And in fact the above PEAR method might not work with google as the smtp host.
Wednesday, December 20, 2017
php - imagemagick for thumbnail gallery making
Just here for reference is a script I used to make a simplistic thumbnail gallery with links to fullsize, full of copy and paste bits from a few sources. You need a folder named thumb/ where it will dump the smaller versions. It uses Imagemagick thumbnailImage to set a maximum size of 300x300 but preserve the aspect ration and then dumps HTML I copy and paste into the index. This is for a gallery archive for my real-world Wall o' Peeps
<?
$skips = "";
$files = array_diff(scandir("."), array('..', '.'));
foreach($files as $i => $justname){
$UPPERCASE = strtoupper($justname);
$UPPERCASE = strtoupper($justname);
if(endsWith($UPPERCASE,".JPG") || endsWith($UPPERCASE,".JPEG") ){
$filename = $justname;
$image = new Imagick($filename);
autorotate($image);
$d = $image->getImageGeometry();
$w = $d['width'];
$image->thumbnailImage(300,300,true);
$image->writeImage("thumb/".$filename);
$d2 = $image->getImageGeometry();
$w2 = $d2['width'];
$h2 = $d2['height'];
print htmlspecialchars("<a href=\"$justname\"><img width=\"$w2\" height=\"$h2\" src=\"thumb/".$justname."\"></a>\n\n");
print "<br><br>\n";
} else {
$skips = $skips . " skip $justname<br><br>";
}
}
print "<br><br>$skips";
function autorotate(Imagick $image)
{
switch ($image->getImageOrientation()) {
case Imagick::ORIENTATION_TOPLEFT:
break;
case Imagick::ORIENTATION_TOPRIGHT:
$image->flopImage();
break;
case Imagick::ORIENTATION_BOTTOMRIGHT:
$image->rotateImage("#000", 180);
break;
case Imagick::ORIENTATION_BOTTOMLEFT:
$image->flopImage();
$image->rotateImage("#000", 180);
break;
case Imagick::ORIENTATION_LEFTTOP:
$image->flopImage();
$image->rotateImage("#000", -90);
break;
case Imagick::ORIENTATION_RIGHTTOP:
$image->rotateImage("#000", 90);
break;
case Imagick::ORIENTATION_RIGHTBOTTOM:
$image->flopImage();
$image->rotateImage("#000", 90);
break;
case Imagick::ORIENTATION_LEFTBOTTOM:
$image->rotateImage("#000", -90);
break;
default: // Invalid orientation
break;
}
$image->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
return $image;
}
function startsWith($haystack, $needle)
{
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
function endsWith($haystack, $needle)
{
$length = strlen($needle);
return $length === 0 ||
(substr($haystack, -$length) === $needle);
}
?>
<?
$skips = "";
$files = array_diff(scandir("."), array('..', '.'));
foreach($files as $i => $justname){
$UPPERCASE = strtoupper($justname);
$UPPERCASE = strtoupper($justname);
if(endsWith($UPPERCASE,".JPG") || endsWith($UPPERCASE,".JPEG") ){
$filename = $justname;
$image = new Imagick($filename);
autorotate($image);
$d = $image->getImageGeometry();
$w = $d['width'];
$image->thumbnailImage(300,300,true);
$image->writeImage("thumb/".$filename);
$d2 = $image->getImageGeometry();
$w2 = $d2['width'];
$h2 = $d2['height'];
print htmlspecialchars("<a href=\"$justname\"><img width=\"$w2\" height=\"$h2\" src=\"thumb/".$justname."\"></a>\n\n");
print "<br><br>\n";
} else {
$skips = $skips . " skip $justname<br><br>";
}
}
print "<br><br>$skips";
function autorotate(Imagick $image)
{
switch ($image->getImageOrientation()) {
case Imagick::ORIENTATION_TOPLEFT:
break;
case Imagick::ORIENTATION_TOPRIGHT:
$image->flopImage();
break;
case Imagick::ORIENTATION_BOTTOMRIGHT:
$image->rotateImage("#000", 180);
break;
case Imagick::ORIENTATION_BOTTOMLEFT:
$image->flopImage();
$image->rotateImage("#000", 180);
break;
case Imagick::ORIENTATION_LEFTTOP:
$image->flopImage();
$image->rotateImage("#000", -90);
break;
case Imagick::ORIENTATION_RIGHTTOP:
$image->rotateImage("#000", 90);
break;
case Imagick::ORIENTATION_RIGHTBOTTOM:
$image->flopImage();
$image->rotateImage("#000", 90);
break;
case Imagick::ORIENTATION_LEFTBOTTOM:
$image->rotateImage("#000", -90);
break;
default: // Invalid orientation
break;
}
$image->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
return $image;
}
function startsWith($haystack, $needle)
{
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
function endsWith($haystack, $needle)
{
$length = strlen($needle);
return $length === 0 ||
(substr($haystack, -$length) === $needle);
}
?>
Wednesday, December 13, 2017
php - download in the browsers, not as a download
In making a band chart/pdf management system, I needed a method for streaming/download files.
PHP makes it pretty easy, but I had to bang around just a bit before I could figure out how to let it try and open stuff in a browser window (vs going to straight to the download folder)
The code ended up a s
if(securityCheck(getLoggedInUser(),$_BAND,false)){
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: '.mime_content_type($file));
#vs application/octet-stream or application/pdf
header('Content-Disposition: inline; filename="'.basename($file).'"');
#vs Content-Disposition: attachment
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
} else {
print "NO LUCK $FILENAME";
}
exit;
} else {
print "UNAUTHORIZED";
exit;
}
The bits that were different from the original example I copy and pasted are in bold.
Also, I learned the power of urldecode() :-D
PHP makes it pretty easy, but I had to bang around just a bit before I could figure out how to let it try and open stuff in a browser window (vs going to straight to the download folder)
The code ended up a s
if(securityCheck(getLoggedInUser(),$_BAND,false)){
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: '.mime_content_type($file));
#vs application/octet-stream or application/pdf
header('Content-Disposition: inline; filename="'.basename($file).'"');
#vs Content-Disposition: attachment
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
} else {
print "NO LUCK $FILENAME";
}
exit;
} else {
print "UNAUTHORIZED";
exit;
}
The bits that were different from the original example I copy and pasted are in bold.
Also, I learned the power of urldecode() :-D
the state of javascript
The State of Javascript 2017 - Angular 1's high "would NOT use again" value is still validating for my prejudices :-D
Tuesday, December 12, 2017
what'd i say? what'd i say? on facebook
I've posted alot about facebook here, including talking about the UI/UX details they get right (that post was from before they added various reaction types instead of the ubiquitous like ... also before the full depth of the echo chamber effect and manipulation by outside parties was known...)
One small thing I've noticed is how if I make a post and then go away from the site, there's a small window of time where if I bounce back to Facebook, my own post is the first thing I see. Whether that's because people like the reassurance of "yes I really just posted that and it went through" or are prone to make comments or edits, it's a terrific little UX detail.
One small thing I've noticed is how if I make a post and then go away from the site, there's a small window of time where if I bounce back to Facebook, my own post is the first thing I see. Whether that's because people like the reassurance of "yes I really just posted that and it went through" or are prone to make comments or edits, it's a terrific little UX detail.
Thursday, December 7, 2017
freemarker boolean weirdness
Freemarker Madness - my coworker helped me learn that
(I also got reminded myself that while ftl functions can't really print to the page, you can assign stuff to a global-ish variable and print it after ... really, log/printf based debugging will always be a crucical tool in testing your assumptions, even when in a system that has a rich step through debugger.)
So the coworker mentioned freemarket documentation warns about that precedence glitch... but man, that's an annoying one! You really don't want your language messing up boolean operations...
<#if ((allYearsForTrimName!false) && (!canShow("f:listings.seoLinksVariantContext")))>`does about what you'd expect (in terms of not executing the block if that canShow routine returns true) but
<#if (allYearsForTrimName!false && (!canShow("f:listings.seoLinksVariantContext")))>(without the safety paren around allYearsForTrimName) always evaluates as true and executes the block.
(I also got reminded myself that while ftl functions can't really print to the page, you can assign stuff to a global-ish variable and print it after ... really, log/printf based debugging will always be a crucical tool in testing your assumptions, even when in a system that has a rich step through debugger.)
So the coworker mentioned freemarket documentation warns about that precedence glitch... but man, that's an annoying one! You really don't want your language messing up boolean operations...
Wednesday, December 6, 2017
the joy of text and todo
In the late 90s there was an online comedy series called "Computer Stew" (Ahead of its time - way before people were putting series on Youtube, or even before there was Youtube)
My favorite episode was a rap tribute to Notepad.exe- but it's the line from the introduction with John Hargraves and Jay on Speakerphone that sticks with me:
My current favorite equivalent of Notepad.exe is Simplenote - it is cloud based but beautifully minimalist, I run the native app on iOS and MacOS (and can get to a webpage with all my info when on someone else's computer.) I love how it makes the first line of text the defacto title (a trick PalmPilots used) and how it never tries to duplicate the font and color of stuff pasted into it (those are two things that its competitor Evernote gets wrong - plus Simplenote never begs me to upgrade.) Also I periodically get Simplenote to let me download a zipfile, my complete archive that I can backup safely, on my own terms.
There's one limitation though - Simplenote's collection is a list, usually ordered by "last modified" (Though it allows you to "pin" notes to the top - I reserve this for a single "scratch" note that I use to transfer text between my laptop and my phone, great when I want to be wordy in a chat program that I don't have for Mac) But going back to Computer Stew, the line about "little todo-lists", plural, suggests Hargrave may have used Notepad in that style where you have multiple windows of the program running, each always open to its todo list, each nestled on its own certain part of the screen, allowing "muscle memory" to kick and in help you keep track of which list is which.
(Of course now "markdown" brings new options for documents that make sense as text or run through to something with a richer layout palette)
That use of Notepad is a digital version of "Post-It notes stuck 'round the cubicle", and in fact Macs come with a "Stickies" app that has a more skeuomorphic view of the same thing. But virtual-stickies-on-PC-screen aren't portable, so these kind of Todo lists have always been secondary to what I keep in a dedicated checklist app - on PalmPilots back in the day, and now on iPhone for the past decade. Checklists are great (epecially since I can set up daily or weekly or monthly recurring reminders) but they are kind of flat, missing the "muscle memory" of a 2D space.
Padlet is a web-based system that lets you do something like the stickies but in a multiple-machine way, by treating the browser as the overall surface area. The program's focus is collaboration, however, so lacks Todo features such as dated and recurring Todos. And while that view is great on a big screen, it doesn't work as well condensed onto a handheld device.
Once upon a time I grabbed to domain name "todo2d.com" as a placeholder for an app that would combine Padlet's sense of a 2D space with fundamental Todo features. Never got around to it, especially since the audience for it is a bit niche. Currently I use Appigo Todo on my phone, which is pretty solid, but I'd love to find a Todo App that allowed multiple list but with an option of viewing all the contents of all lists at once. (For example, I'd love a separate list for stuff where I'm waiting for someone else, or that needs to be done at home or at a specific store...
My favorite episode was a rap tribute to Notepad.exe- but it's the line from the introduction with John Hargraves and Jay on Speakerphone that sticks with me:
"Well Jay, it's getting to be that time of year when everyone is giving out their awards for best software..."
"That's right! Best office suite, best paint program! Best online game!"
"And you know which program would win hands-down in my book..."
"Unfortunately I do sir."
"It's Notepad, that little text editor that comes with Window? I love that thing, man!"
"I know you do. I can't understand it. I think you're insane. Folks, John manages the entire show with little todo-lists inside of NOTEPAD!"
"I love Notepad! It's small, it never crashes... they never add any features to it... in fact I think I feel a song coming on..."
My current favorite equivalent of Notepad.exe is Simplenote - it is cloud based but beautifully minimalist, I run the native app on iOS and MacOS (and can get to a webpage with all my info when on someone else's computer.) I love how it makes the first line of text the defacto title (a trick PalmPilots used) and how it never tries to duplicate the font and color of stuff pasted into it (those are two things that its competitor Evernote gets wrong - plus Simplenote never begs me to upgrade.) Also I periodically get Simplenote to let me download a zipfile, my complete archive that I can backup safely, on my own terms.
There's one limitation though - Simplenote's collection is a list, usually ordered by "last modified" (Though it allows you to "pin" notes to the top - I reserve this for a single "scratch" note that I use to transfer text between my laptop and my phone, great when I want to be wordy in a chat program that I don't have for Mac) But going back to Computer Stew, the line about "little todo-lists", plural, suggests Hargrave may have used Notepad in that style where you have multiple windows of the program running, each always open to its todo list, each nestled on its own certain part of the screen, allowing "muscle memory" to kick and in help you keep track of which list is which.
(Of course now "markdown" brings new options for documents that make sense as text or run through to something with a richer layout palette)
That use of Notepad is a digital version of "Post-It notes stuck 'round the cubicle", and in fact Macs come with a "Stickies" app that has a more skeuomorphic view of the same thing. But virtual-stickies-on-PC-screen aren't portable, so these kind of Todo lists have always been secondary to what I keep in a dedicated checklist app - on PalmPilots back in the day, and now on iPhone for the past decade. Checklists are great (epecially since I can set up daily or weekly or monthly recurring reminders) but they are kind of flat, missing the "muscle memory" of a 2D space.
Padlet is a web-based system that lets you do something like the stickies but in a multiple-machine way, by treating the browser as the overall surface area. The program's focus is collaboration, however, so lacks Todo features such as dated and recurring Todos. And while that view is great on a big screen, it doesn't work as well condensed onto a handheld device.
Once upon a time I grabbed to domain name "todo2d.com" as a placeholder for an app that would combine Padlet's sense of a 2D space with fundamental Todo features. Never got around to it, especially since the audience for it is a bit niche. Currently I use Appigo Todo on my phone, which is pretty solid, but I'd love to find a Todo App that allowed multiple list but with an option of viewing all the contents of all lists at once. (For example, I'd love a separate list for stuff where I'm waiting for someone else, or that needs to be done at home or at a specific store...
gesture interfaces and the tao of programming
Rant at work: "@##!@# Somehow I used some magic gesture on my touchpad, and now Slack is zoomed in - but its not the normal zoom I can adjust with cmd-+ and cmd-- and cmd-0 ; instead it's just decided that slack should be in a magic window that I can pan back and forth with two finger swiping, but I have no idea of how to make everything fit" (restarting Slack seemed to fix it though it felt like I had to do so twice.)
Gestures where an errant side of a finger creates radical behaviors violates this part of Tao of Programming:
Gestures where an errant side of a finger creates radical behaviors violates this part of Tao of Programming:
A program should follow the "Law of Least Astonishment". What is this law? It is simply that the program should always respond to the user in the way that astonishes him least.BTW, the Tao of Programming is brilliant - it's weirdly authentic, like I've seen other things that parody the form of the Tao Te Ching but they generally don't also say smart things about their subject matter....
Tuesday, November 28, 2017
paper airplane as "send"
Can I just say, I love the "paper airplane" arrow chevron that means send in a few mobile apps? It's so cute, and still works even if you don't recognize it, since it still carries a sense of motion / go / send.
Monday, November 27, 2017
Saturday, November 18, 2017
a hack to avoid iOS music app's odd decision to play the first thing that comes to mind...
I try to be forgiving of UI/UX decisions that seem foolish to me, understanding that sometimes there are design constraints I might not be aware of that are driving the show.
One longstanding iOS annoyance/oddity is this: when a podcast or audiobook ends, control tends to revert back to the music app, which makes sense, but then the music app starts playing songs from its library, starting at the top, alphabetically.
I suppose that's better than playing from all songs on shuffle, since at least I got to recognize that the Jackson's Five "A.B.C." meant it was time to switch gears, but for me it's worse than say, picking up where I left off, playlist-wise. Or better yet - why play anything? Is silence so awful? Would people assume the chain from device to speaker has somehow busted if there's nothing but quiet?
So, a small hack - moviesoundclips.net has a page of clips from Aliens, I took "That's it, man. Game over, man, game over!", padded it to be that plus four minutes of silence, called it "a a a - game over man" and then made an mp3 of the result. A little playlist hackery to make sure it had a spot on my phone and I was set - now when podcasts ends I should hear that, rather than the Jackson 5 (don't get be wrong, it's a great song, but kind of loud...)
One longstanding iOS annoyance/oddity is this: when a podcast or audiobook ends, control tends to revert back to the music app, which makes sense, but then the music app starts playing songs from its library, starting at the top, alphabetically.
I suppose that's better than playing from all songs on shuffle, since at least I got to recognize that the Jackson's Five "A.B.C." meant it was time to switch gears, but for me it's worse than say, picking up where I left off, playlist-wise. Or better yet - why play anything? Is silence so awful? Would people assume the chain from device to speaker has somehow busted if there's nothing but quiet?
So, a small hack - moviesoundclips.net has a page of clips from Aliens, I took "That's it, man. Game over, man, game over!", padded it to be that plus four minutes of silence, called it "a a a - game over man" and then made an mp3 of the result. A little playlist hackery to make sure it had a spot on my phone and I was set - now when podcasts ends I should hear that, rather than the Jackson 5 (don't get be wrong, it's a great song, but kind of loud...)
Tuesday, November 14, 2017
wrapping text on a curve path in processing
I made a Processing app to help generate this tuba bell cover for my band BABAM!:
Slightly hackier than usual (I was watching football and hanging out with my Aunt), I used Processing vs p5.js (like I did for my Halloween skull bell cover) because of some fumbling getting p5 to load the font I needed. (Gill Sans Ultra Bold)
Source and supporting files here.
My code started from this Processing forums post but improves it by having the top go around clockwise and the bottom counter clockwise, rather than just do a full 360 around a circle and making people strain their neck to read it. Then there was a lot of fiddling by hand to get the circle sizes to look right and the words to be in the right place.
A better version would take the actual letter width into account - this one is implicitly acting as if it were a monospace font, but I just wanted to get through it.
UPDATE: I improved the program a bit (including a trick to let me use the mouse to fine tune some of the positioning) for the New Magnolia Jazz Band:
Source and Supporting PDE files here.
UPDATE TO THE UPDATE: I further fixed the file to let me make a transparent background for it, so I could more easily do the coloring. (The trick is to use Processing's PGraphics to make an offscreen version. And when you do that remember your dang .beginDraw() and .endDraw();, along with moving all the actual drawing functions to be references to the offscreen graphic.)
PROTIP FOR THE UPDATE TO THE UPDATE: I admit this is kind of obvious to everyone whose head isn't stuck in old school pixels like mine can be, but if you use layers properly, color the layer behind the art rather than try to floodfill in the layer itself..
UPDATE TO THE UPDATE TO THE UPDATE: the code is getting increasingly messy, but I made a new version with a blank area in the middle so I can use a washable marker to promote the message of the group my band is supporting (we want it to be more about the activism than the band.)
PDE Source Code
Slightly hackier than usual (I was watching football and hanging out with my Aunt), I used Processing vs p5.js (like I did for my Halloween skull bell cover) because of some fumbling getting p5 to load the font I needed. (Gill Sans Ultra Bold)
Source and supporting files here.
My code started from this Processing forums post but improves it by having the top go around clockwise and the bottom counter clockwise, rather than just do a full 360 around a circle and making people strain their neck to read it. Then there was a lot of fiddling by hand to get the circle sizes to look right and the words to be in the right place.
A better version would take the actual letter width into account - this one is implicitly acting as if it were a monospace font, but I just wanted to get through it.
UPDATE: I improved the program a bit (including a trick to let me use the mouse to fine tune some of the positioning) for the New Magnolia Jazz Band:
Source and Supporting PDE files here.
UPDATE TO THE UPDATE: I further fixed the file to let me make a transparent background for it, so I could more easily do the coloring. (The trick is to use Processing's PGraphics to make an offscreen version. And when you do that remember your dang .beginDraw() and .endDraw();, along with moving all the actual drawing functions to be references to the offscreen graphic.)
PROTIP FOR THE UPDATE TO THE UPDATE: I admit this is kind of obvious to everyone whose head isn't stuck in old school pixels like mine can be, but if you use layers properly, color the layer behind the art rather than try to floodfill in the layer itself..
UPDATE TO THE UPDATE TO THE UPDATE: the code is getting increasingly messy, but I made a new version with a blank area in the middle so I can use a washable marker to promote the message of the group my band is supporting (we want it to be more about the activism than the band.)
PDE Source Code
Thursday, November 9, 2017
mockupulator: simple rectangle drag and drop in p5.js
So random p5.js thing / tool that has a small chance of being useful for someone else: I was wanting to do simple but parsnickety layout mockups, but Acorn (my default image maniuplator) was being weird and I don't have Sketch (or much Sketch mojo) so I multitasked during the UI talk to make https://kirkdev.alienbill.com/2017/mockupulator/ - it's based on memories of an old "magnetic poetry" java applet I saw, you can just drag and drop the texty bits in the middle to where you want them.
Since the ENTIRE app is just the source code of that page (plus p5.js and the images I slice and dice) it would be easy to apply it to your own sliced up mockup.
Future versions I might never get to would make it save/restore state of the blocks and maybe make up blocks based on text rather than pre-existing image slices.
To quote Douglas Adams:
Since the ENTIRE app is just the source code of that page (plus p5.js and the images I slice and dice) it would be easy to apply it to your own sliced up mockup.
Future versions I might never get to would make it save/restore state of the blocks and maybe make up blocks based on text rather than pre-existing image slices.
To quote Douglas Adams:
I have a well-deserved reputation for being something of a gadget freak, and am rarely happier than when spending an entire day programming my computer to perform automatically a task that would otherwise take me a good ten seconds to do by hand.Ok, more like 50 minutes to save 10 minutes, but still!
Monday, November 6, 2017
"ExtJS was a mistake. One we, as a society, may never recover from."
When I worked at Pearson's startup Alleyoop around 2010 or so, we had an incredibly bright intern named Slater, currently CTO of his own machine learning startup.
On FB he posted
ExtJS was a mistake. One we, as a society, may never recover from.
Our dialog proceeded as follows
Kirk:
Oh my goodness. I thought you would be hipper and getting into the game after ExtJS was recognized as a mistake!
It's funny, I talked with this one engineer I respect but never should have been making UI architectural decision - In 2008 he put us on "Wicket"... all this client-y looking code is in Java, and you have to carefully keep synchronized these divs in the template and their corresponding nested Java objects... blargh)
In 2015 I was talking to him about his shoehorning us into Wicket, and what he, as a primarily serverside guy, would like in a UI. It turns out mostly he wanted to not have to learn HTML5 and especially CSS. "I want to be able to just ask for a button - programatically - and it gives me a button."
I realized ExtJS was probably an extension of this kind of thinking.
It was hard to learn and understand (rather opaque!) and also hard to debug in the browser - all these little widgets with absolute positioning and then given exact pixel coordinates... I'm sure they were doing some cool stuff early in a cross-browser way but man.
The hoops some engineers go through - the extravagant toolkist they then have to learn - to avoid learning some very basic stuff...
Slater:
In a bizarre twist of fate, I recently picked up react + redux (not that I know what I'm doing on the frontend), and then saw an ExtJS stack. Suffice it to say I was horrified, but I think you're spot on about the logic.
I get not wanting to learn a new toolset (one could argue that a lot of cases where node gets extended beyond its sweet spots is another flavor of this same thinking), but anyone should be able to recognize when a framework is pushing them toward unmaintainable code.
Kirk:
A lot comes down to if developers prefer to understand things bottom up / via reductionism (in which case they value very transparent tools - even if they have to write a lot of the grunt work code themselves - the code base might get gnarly and complex but at least it's complexity specific to the application at hand) or top down / holism (in which case they might prefer a richer toolset that makes it easier to write in broad strokes - if the toolset is harmonious with the problem that's great, but chances are the toolset solves MANY people's problems, not just yours, so if you have to track down a mysterious 'whyd this go wrong?' it can be brutal)
Besides the holism/reductionism split, there's also nouns vs verbs; devs like me prefer verbs - I want to describe what I want done, in a more or less procedural way-- whereas noun thinkers want to make something that knows how to do the stuff. So here I like react, where the nouns (the JSX bits embedded in code) live sprinkled among the verbs of code, and you can put in a very relevant breakpoint, or inspect the state. Vs ExtJS, where you specify the nouns and it tries to write the verbs to get you there, and god help if you need to track down how the nouns got to the state they did.
When it comes to toolkits, my heart lies with vanilla-js or DOM manipulators like jQuery - I cut my teeth on caveman CGI, where the DOM was a very static thing that came back in one fell swoop from the server. When I added jQuery into the mix, the verbs I could do in the client multiplied, but the DOM was still that static noun thing, changing only at certain points which were more or less easy to track. Over the years I've felt validated that this is a pretty strong paradigm, as I've seen toolkits come and go, but React has come closest to feeling what might be the actual future, and I don't want to jeopardize my career by sounding like a cranky old stuck in the past dude.
Slater:
Kirk, also I agree with this a lot. Personally I don't feel the noun/verb distinction super clearly, but that's probably because I switch between functional and OO paradigms a lot. I think especially for people coming from a noun-centric (read: Java) environment, learning the thought process behind a generally more verb-centric (read: javascript) environment can be very tough.
Additionally, I'm super, super reductionist. I like tools, and I love standing on the shoulders of giants, but I tread very cautiously when it comes to adopting a new framework, and it's very important to me to understand exactly where the limits of the tool are. I really enjoy APIs for this reason, and I think there are a lot of heavy frameworks that go the wrong way on this (for me at least) by enforcing large amounts of state, and making it unclear how you would extend the code.
I think jQuery is a really simple and straightforward way to built a project. I think that it's quick to pick up, fast to prototype with and for small projects there's nothing wrong with it. My gap comes from the perspective of maintainability. There are a lot of code smell niceties in something like python, generally focusing around clean code abstractions, decoupling data from logic, etc... that can be really hard to get right in plain jQuery. Even something like splitting code up into multiple files and using functions across them can get surprisingly gross pretty quickly just because of how the namespaces are set up.
I think that's the biggest advantage of React (I'll say Angular is pretty damn close to React, and from a macro-level they're almost interchangeable. Angular just feels like a bigger, clunkier version of React to me). Very clear separation of concerns. Clear lines of responsibility, and built for re-usability.
Kirk:
Enjoying this dialog Slater.
The way you used Java/Javascript as your example for noun v verb pinged something in me: I know the shared "Java" in the name is coincidental-ish but there's the "script" part - and that being verb-centric - "I want the computer to do this". It made me think of a defense of PHP I reread recently - Taking PHP Seriously - besides talking about how the odd dichotomy where the language is much reviled yet so many big league projects got at least their start in it - and in looking for some answers, mentioning that the script-ish way it gives you a "from-scratch" clean envioronment for each web invocation - that's actually tremendously powerful.
I can't always separate my long-standing biases (and roots doing personal projects where I was the only developer, though some of those codebases have lasted me decades, literally) from my "graybeard" experience, but I feel like I haven't seen much many systems that were clearly easier to understand and bring more people on later via leveraging higher level things. Whatever advantages were gained in using boilerplate were more than eaten away by a combination of new developers having less experience with the platform and generally being MUCH harder to debug from a bottom-up way. (i.e. completely opaque stack traces etc)
For namespaces... I dunno. Just throw everything into window.MyProjectName = {} and be done with it ;-D
The fact Angular and React feel so similar to you indicates that we view things very differently (admittedly I've done more hacking in Angular and only toy things in React so far). Elio who is posting here loves Angular, I hate it - who thought "client-JSP" would be a good idea? That is to say, "we love writing tags so much, we're going to let you make your whole View in nothing but tags - never mind how much syntax you have to learn in both the tagspace AND the plumbing that lets it all look like html tags. But more to the heart of our conversation, for me tags are nouns, the fairly static nouns of the DOM, so trying to make them all verb-y with loops and conditionals, and so it's blend of "this is a tag for the browser to render, this is a tag for the view's logic" feels strange to me. By putting the noun tags in the verb logic, React does something a little similar at a high level, but at least the logic and the display are in distinct languages (js vs jsx)
Heh, reminds me of How it feels to learn JavaScript in 2016 . Probably a warning sign that I still think more like a prototype cowboy is how much I looove just writing an index.html, a style.css, and app.js (and then I sort of love that "ok so put it all in one document anyway" paradigm ;-)
Anyway, very smart guy, one to watch out for.
Wednesday, November 1, 2017
iphone x is gestures all the way down
Slate: The iPhone X Takes Hand Gestures Too Far
I've never been a fan of gesture interfaces, and life with an iPhone X requires a whole new slew of them. (Conversely, I've always been a big fan of the home button; an easy to access "lets take it from the top" escape hatch can be enormously centering, like how when I click on a website's name in their header on any page I go to the site homepage.)
That's one of problems with the designer's dream of being such world-beater designers they they can stop having to actually, you know, design- having achieved the zen of "a piece of featureless glass"... the phone then just consists of an OS and apps - but having to designate certain finger moves as "ok, now you're communicating with the OS" makes a less known reliable channel, and one prone to accidental invocations, and takes away from the language of gestures that apps are allowed to use.
I've never been a fan of gesture interfaces, and life with an iPhone X requires a whole new slew of them. (Conversely, I've always been a big fan of the home button; an easy to access "lets take it from the top" escape hatch can be enormously centering, like how when I click on a website's name in their header on any page I go to the site homepage.)
That's one of problems with the designer's dream of being such world-beater designers they they can stop having to actually, you know, design- having achieved the zen of "a piece of featureless glass"... the phone then just consists of an OS and apps - but having to designate certain finger moves as "ok, now you're communicating with the OS" makes a less known reliable channel, and one prone to accidental invocations, and takes away from the language of gestures that apps are allowed to use.
Thursday, October 26, 2017
moment of duh: search across ALL javascript in chrome dev tools
I was a little nonplussed at not seeing a widget (or having my normal keyboard shortcuts seem to work) for search ALL the scripts for a bit of javascript... but if you click on the vertical ... box under developer tools, there's a "Search all files".
Wednesday, October 25, 2017
clever UI for tight quarters and fat fingers
One of the few paper puzzle games I like goes by several different names - Nonograms, Picross (Nintendo calls it that in its digital versions), Paint-By-Number... actually the Wikipedia Page lists about 30 names for it- I feel like the branding is one of the reasons why it hasn't reached the popularity of, say, Sudoku - for my money, uncovering a small pixelated picture is immensely more satisfying than making yet another grid of numbers.
If you don't know, Picross gives you an empty grid, and then numbers for the rows and columns - so if a grid is 10 squares wide, and to the side of that row it says "10" you know the whole row is filled in - or if says "5 4" you know it's 5 squares and then 4 squares, and since there must be a blank between, you know the pattern exactly, since 5 + (1 blank) + 4 = 10. Usually there's some ambiguity, like "1 3 1" and you have to cross correlate with the columns to figure out what's going on exactly
I found a decent version for iOS called "Picture Cross" - it's free, but for a any purchase (as cheap as 99 cents) the annoying advertisements go away. By default a simple puzzle in it looks something like this:
The UI is pretty easy, the center "Fill In" and "Blank X" buttons are modals - the checkbox shows you which mode it's in - click any empty square, or drag across squares, and it gets filled in with marks or blanks.
That's tricky once you start getting more squares (15x15 or more) or if you have a small phone, so they include an "Alternate" control scheme that takes a second to figure out but then works really well. The mode adds a "pin" button and a cursor:
The nice thing is, the cursor moves from its location no matter where on the screen you swipe, so you can see exactly what square you're talking about it without your finger blocking it. So from there you can go ahead and click the Mark or X button and it toggles the state of the box under the cursor.
In this mode, the pin button is what's modal. Click it, drag-swipe, and an entire region is selected, with the first square being where the cursor started: (in this case, according to the rules of Picross, we know that all 10 squares along the bottom are due for being marked...)
If you don't know, Picross gives you an empty grid, and then numbers for the rows and columns - so if a grid is 10 squares wide, and to the side of that row it says "10" you know the whole row is filled in - or if says "5 4" you know it's 5 squares and then 4 squares, and since there must be a blank between, you know the pattern exactly, since 5 + (1 blank) + 4 = 10. Usually there's some ambiguity, like "1 3 1" and you have to cross correlate with the columns to figure out what's going on exactly
I found a decent version for iOS called "Picture Cross" - it's free, but for a any purchase (as cheap as 99 cents) the annoying advertisements go away. By default a simple puzzle in it looks something like this:
The UI is pretty easy, the center "Fill In" and "Blank X" buttons are modals - the checkbox shows you which mode it's in - click any empty square, or drag across squares, and it gets filled in with marks or blanks.
That's tricky once you start getting more squares (15x15 or more) or if you have a small phone, so they include an "Alternate" control scheme that takes a second to figure out but then works really well. The mode adds a "pin" button and a cursor:
The nice thing is, the cursor moves from its location no matter where on the screen you swipe, so you can see exactly what square you're talking about it without your finger blocking it. So from there you can go ahead and click the Mark or X button and it toggles the state of the box under the cursor.
In this mode, the pin button is what's modal. Click it, drag-swipe, and an entire region is selected, with the first square being where the cursor started: (in this case, according to the rules of Picross, we know that all 10 squares along the bottom are due for being marked...)
Then you can click the "Mark" button, and all the square are set.
So when you have a base like that in Picross, the next step is easy, since we've anchored all the bottom numbers along the top - the 1 and1 is set, the 4 and the 6 we know how they'll go.. so I'll unpin the selection, and go to the column of the 4..., pin and drag...
Mark as filled...
Here's a detail that's cool - I know there's nothing but the 4 in that column, so I can should the rest with X blanks. I just drag up to the top of the column, and hit the X button...
The clever part of this UI is that it doesn't undo what I've already marked. I feel like a more naive UI would just do "whatever is selected, change its state" but Picross encourages being conservative in how you mark things, so you don't want them casually unmarked - that would be super confusing.
That "don't change state directly from marked to X" rule applies even if you're checking squares one by one, for similar reasons.
techie cheatsheets galore!
https://devhints.io/ - TL;DR for developer documentation
"A ridiculous collection of web development cheatsheets"
"A ridiculous collection of web development cheatsheets"
Monday, October 23, 2017
sublime - autosave when you change apps
The new hotness standard brought by IntelliJ seems to be autosave, cmd-s is old and busted. For a while I thought this was black magic but I think sometimes it's as simple as a "save when you lose focus".
I still use sublime for other work, and among some other tips this page tells me I can just add
"save_on_focus_lost": true
to my preferences>settings (currently set to
{
"auto_match_enabled": false,
"color_scheme": "Packages/Color Scheme - Default/Solarized (Light).tmTheme",
"font_size": 15,
"auto_close_tags": false,
"save_on_focus_lost": true
}
for what it's worth.
From a UX standpoint, autosave is kind of an paradigm shift, but in an age of really good cmd-z undo, it's not as problematic as my old self would have thought.
I still use sublime for other work, and among some other tips this page tells me I can just add
"save_on_focus_lost": true
to my preferences>settings (currently set to
{
"auto_match_enabled": false,
"color_scheme": "Packages/Color Scheme - Default/Solarized (Light).tmTheme",
"font_size": 15,
"auto_close_tags": false,
"save_on_focus_lost": true
}
for what it's worth.
From a UX standpoint, autosave is kind of an paradigm shift, but in an age of really good cmd-z undo, it's not as problematic as my old self would have thought.
Friday, October 20, 2017
a little light reading
Articles on stuff my company is looking into...
Oh and https://www.fullstackreact.com/30-days-of-react/ and https://learnredux.com/ oh and then https://cssgrid.io/
- https://medium.com/@addyosmani/progressive-web-apps-with-react-js-part-i-introduction-50679aef2b12
- https://developers.google.com/web/fundamentals/performance/prpl-pattern/
- http://redux.js.org/docs/basics/UsageWithReact.html
- https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
- https://blogs.akamai.com/2017/03/http2-server-push-the-what-how-and-why.html
- http://redux.js.org/docs/recipes/ServerRendering.html
- https://developers.google.com/web/fundamentals/architecture/app-shell
- https://developers.google.com/web/fundamentals/push-notifications/
- https://developers.google.com/web/fundamentals/web-app-manifest/
- https://developers.google.com/web/fundamentals/performance/rendering/
Oh and https://www.fullstackreact.com/30-days-of-react/ and https://learnredux.com/ oh and then https://cssgrid.io/
local music fingerprinting in 1/2 a GB?
Huh - Google's Pixel 2 does Shazam/Soundhound style audio fingerprinting locally? And that db for that is only half a GB? I never would have suspected the numbers were like that (even knowing factoids how like a simple formula "does the subsequent note in series go up or down, note by note makes a unique key for melodies in a surprisingly small number of notes")
Thursday, October 19, 2017
more quick and dirty php: finding matching tags
Just here for my future reference - did another "PHP as command line scripting tool to recursively go over directories", this one to get a rough idea of, in our Freemarket Template codebase, how many
<#macro fooBar blah blah>
macro definitions were orphans, and never called with a
<@foobar blah blah >
tag. (Freemarker is hella flexible in terms of how you build a macro name to call, though, so this is far from bullet proof...)
<?
$directory = "/Users/kisrael/cargurus/cg-main/cargurus-site-static/templates/";
$macroDefLocations = array();
$macroCallLocations = array();
#checkfile("inventorylisting/ocrFilterMacros.ftl");
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
while($it->valid()) {
$pathname = $it->getSubPathName();
if (endsWith($pathname, ".ftl")) {
checkfile($pathname);
}
$it->next();
}
function checkfile($pathname){
global $macroDefLocations, $macroCallLocations;
lookForMatches($pathname,$macroDefLocations,"/\<\#macro.*?\>/s","/\<\#macro\s(\w*)\W/s");
lookForMatches($pathname,$macroCallLocations,"/\<\@.*?\>/s","/\<\@(\w*)\W/s");
}
foreach($macroDefLocations as $definedMacro => $usages){
if(! array_key_exists($definedMacro, $macroCallLocations)){
$file = array_keys($macroDefLocations[$definedMacro])[0];
print "[".$definedMacro."]\t".$file."\n";
$orphanedMacrosInFile[$file]++;
$orphan++;
} else {
$calledMacrosInFile[$file]++;
$wellcalled++;
}
}
#print "$orphan orphans $wellcalled called\n";
#print "CALLED:\n";
#print_r($calledMacrosInFile);
#print "ORPHANED:\n";
#print_r($orphanedMacrosInFile);
foreach($orphanedMacrosInFile as $file => $countOrphans){
$countCalled = 0;
if(array_key_exists($file, $calledMacrosInFile)){
$countCalled = $calledMacrosInFile[$file];
}
$percentOrphans[$file] = (100.0 * ($countOrphans) / ($countCalled +$countOrphans ));
$orphanDesc[$file] = "$countOrphans orphans / ".($countCalled +$countOrphans)." defs";
}
arsort($percentOrphans);
$keys = array_keys($percentOrphans);
foreach($keys as $file){
print round($percentOrphans[$file],3)."\t".$orphanDesc[$file]."\t".$file."\n";;
}
function lookForMatches($pathname,&$results,$regexFind,$regexExtract){
global $directory;
$content = file_get_contents($directory.$pathname);
preg_match_all($regexFind,$content,$matches);
foreach ($matches[0] as $key => $tag){
preg_match($regexExtract,$tag,$finds);
$macro = $finds[1];
#print "vvv\n$tag\n^^^\n";
if (! array_key_exists($macro,$results)){
$results[$macro] = array();
}
if (! array_key_exists($pathname,$results[$macro])){
$results[$macro][$pathname] = 0;
}
$results[$macro][$pathname]++;
}
}
function endsWith($haystack, $needle) {
return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0 && strpos($haystack, $needle, $temp) !== false);
}
?>
<#macro fooBar blah blah>
macro definitions were orphans, and never called with a
<@foobar blah blah >
tag. (Freemarker is hella flexible in terms of how you build a macro name to call, though, so this is far from bullet proof...)
<?
$directory = "/Users/kisrael/cargurus/cg-main/cargurus-site-static/templates/";
$macroDefLocations = array();
$macroCallLocations = array();
#checkfile("inventorylisting/ocrFilterMacros.ftl");
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
while($it->valid()) {
$pathname = $it->getSubPathName();
if (endsWith($pathname, ".ftl")) {
checkfile($pathname);
}
$it->next();
}
function checkfile($pathname){
global $macroDefLocations, $macroCallLocations;
lookForMatches($pathname,$macroDefLocations,"/\<\#macro.*?\>/s","/\<\#macro\s(\w*)\W/s");
lookForMatches($pathname,$macroCallLocations,"/\<\@.*?\>/s","/\<\@(\w*)\W/s");
}
foreach($macroDefLocations as $definedMacro => $usages){
if(! array_key_exists($definedMacro, $macroCallLocations)){
$file = array_keys($macroDefLocations[$definedMacro])[0];
print "[".$definedMacro."]\t".$file."\n";
$orphanedMacrosInFile[$file]++;
$orphan++;
} else {
$calledMacrosInFile[$file]++;
$wellcalled++;
}
}
#print "$orphan orphans $wellcalled called\n";
#print "CALLED:\n";
#print_r($calledMacrosInFile);
#print "ORPHANED:\n";
#print_r($orphanedMacrosInFile);
foreach($orphanedMacrosInFile as $file => $countOrphans){
$countCalled = 0;
if(array_key_exists($file, $calledMacrosInFile)){
$countCalled = $calledMacrosInFile[$file];
}
$percentOrphans[$file] = (100.0 * ($countOrphans) / ($countCalled +$countOrphans ));
$orphanDesc[$file] = "$countOrphans orphans / ".($countCalled +$countOrphans)." defs";
}
arsort($percentOrphans);
$keys = array_keys($percentOrphans);
foreach($keys as $file){
print round($percentOrphans[$file],3)."\t".$orphanDesc[$file]."\t".$file."\n";;
}
function lookForMatches($pathname,&$results,$regexFind,$regexExtract){
global $directory;
$content = file_get_contents($directory.$pathname);
preg_match_all($regexFind,$content,$matches);
foreach ($matches[0] as $key => $tag){
preg_match($regexExtract,$tag,$finds);
$macro = $finds[1];
#print "vvv\n$tag\n^^^\n";
if (! array_key_exists($macro,$results)){
$results[$macro] = array();
}
if (! array_key_exists($pathname,$results[$macro])){
$results[$macro][$pathname] = 0;
}
$results[$macro][$pathname]++;
}
}
function endsWith($haystack, $needle) {
return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0 && strpos($haystack, $needle, $temp) !== false);
}
?>
Tuesday, October 10, 2017
The one true Javascript exception handler:
try {
something
} catch(e) {
window.location.href = "http://stackoverflow.com/search?q=[js] +" e.message;
}
(via)
try {
something
} catch(e) {
window.location.href = "http://stackoverflow.com/search?q=[js] +" e.message;
}
(via)
Monday, October 2, 2017
what perl taught me (in the early-mid-90s)
Perl was the first language I learned after the "C" that was taught in my Intro to Computer Science class (so probably my fourth language overall, after BASIC and Logo growing up.)
Compared to C of that era, it was like manna from heaven. Here's what it gave me that C lacked:
Compared to C of that era, it was like manna from heaven. Here's what it gave me that C lacked:
- Maps, or as Perl called them, Associative Arrays. Key/Value pairs are such an ENORMOUSLY powerful concept, and this was my first exposure to them.
- Regular Expressions - being able to match strings is also very powerful and fundamental
- Strings as first class entities - compared to C saying "you can make a string as an array of strings ending with a \0 character", this was great. In this way too, C was oddly inferior to the BASICs I grew up with.
- Duck-Typing. I'm not as sold on the importance of duck-typing as I used to be, but I liked it. I especially like Perl's habit of quiet context-based assumptions... assuming false-y or null things are zero if you're trying to do math, compared to Javascripts tendency to panic with "NaN" is more robust and less error-prone than some purists probably realize.
- Not having to micromanage memory. Man, was C annoying in this regard; having to carefully de-allocate every hunk of memory explicitly was a real pain. (In retrospect, Perl may have gotten away with this more because of its "script" feeling... in general things that do some work than quit can be more forgiving than long-running processes...
Such a breath of fresh air... and then when I learned I could rent cheap webspace that would let me run Perl and that CGI mapped fairly wonderfully to that Key/Value concept... the sky was the limit. I could do nearly anything the web could do.
I was shocked a decade later when a coworker pointed out that most of that stuff I loved about Perl was actually from its history as a glue language, wrapping C(!)-based Unix system calls. That stunned me... I knew the wonky syntax came from Perl's history as a bit of a mongrel, but compared to other stuff (especially early PHP) it always felt very professional, you hardly ever saw the skeleton, and there wasn't much in it that was obviously for the convenience of the language implementer rather than the programmer. And all these new tricks felt like the very opposite of C...
These days, my Perl is decidedly rusty. PHP eventually matured and having all the stuff for webtasks baked into the language rather than the ugliness of CPAN module compiling was great. Also I'm worried I should have been learning Python, rather than blowing it off because the given task was easier in more familiar Perl. And I never did get my head wrapped around Perl's flavor of Object Oriented thinking. Still, I'll always be grateful to it for opening my eyes to new ways of thinking about programming, which is what the worthy languages and frameworks do.
quick jquery check for repeated / duplicate ids in DOM
Coworker suggested this console-code proof that ids were being duplicated:
$('[id]').each(function(){
var ids = $('[id="'+this.id+'"]');
if(ids.length>1 && ids[0]==this)
console.warn('Multiple IDs #'+this.id);
});
If I was less lazy I'd cook up the non-jQuery version.
$('[id]').each(function(){
var ids = $('[id="'+this.id+'"]');
if(ids.length>1 && ids[0]==this)
console.warn('Multiple IDs #'+this.id);
});
If I was less lazy I'd cook up the non-jQuery version.
Tuesday, September 26, 2017
p5 editor to programmatically make a downloadable image in the browser
QUICK HACK: I have a lot of decent art programs for my iPad, but it seems like like such programs no longer come with tools for making simple shapes, like circles and squares. That made my inner child a little sad! Paint programs used to be fun, you know? Like just tooling around throwing shapes and patterns on the screen...
It's tough enough to find programs with flood fill, actually.... it seems like almost every art program is either imitating "real" art tools, aimed at photo retouching, or tries to get you to think in their weird interface for vectors.
Anyway, I wanted a colored circle as a base for a Jack-O-Lantern tuba cover I'm making, and the easiest way was to go to the online p5 editor and enter the following:
function setup() {
createCanvas(3000, 3000);
noSmooth();
}
function draw() {
background(255);
noStroke();
fill(255,140,0);
ellipse(1500,1500,2500,2500);
saveCanvas("art.png");
noLoop();
}
That actually generates a file "art.png" which the browser will offer to download.
I then used Dropbox to transfer it to my iPad.
UPDATE: Did a bit more work...
So, I'm making up designs for tuba covers (as printed by the fine folks at http://stretchablecovers.net/) Before I mentioned the first step for a Jack-O-Lantern (drawn in by hand)
But a few years ago I had a DIY craft project (felt crudely stapled onto the blank side of a cover) skull for my tuba:
I lost it in my recent move, and decided I'd get it printed up, and that I'd use another p5.js program to do it. (I liked the idea of using shapes rather than being quite hand drawn).
Net, net, you can see and play with the p5.js program here. The moderately clever bit is using a technique to let me finesse the placement of a piece via "map()"ing the X and Y mouse position to appropriate values, and then building a string for the corresponding ellipse() or line() command. I then both print the screen and run javascript's "eval()" on it. Once I get the fragement where I like it, I copy and paste the printed version of the statement and place it into the code, so it's kind of baked into the place and then I repeat the technique for the next fragment.
Oh, also for funsies check out my old Carve Your Own Jack-O-Lantern Processing Program... very similar in spirit to all this.
For reference (since I guess I shouldn't an account on a "alpha version" editor as a code repository) here is the code... it's not (quite) as hackish as it looks.
var BASE;
var s;
function setup() {
BASE = 30;// random(38,58);
createCanvas(BASE*10, BASE*10);
strokeCap(SQUARE);
}
function draw() {
//BASE += random(-.1,.1);
background(200);
noStroke();
translate(width/2,height/2);
fill(255);
ellipse(0,0,BASE*8,BASE*8);
fill(0);
push();
var f1 = map(mouseX,0,width, -5,5).toFixed(3);;
var f2 = map(mouseY,0,height, -5,5).toFixed(3);
var s;
//var s = "bigEye("+f1+","+f2+");";
s = "bigEye(1.835,-1.239);";
eval(s);
s = "nose(-1.867,0.824)";
eval(s);
//s = "smallEye("+f1+","+f2+")";
s = "smallEye(-2.037,-0.947)";
eval(s);
s = "mouth()";
eval(s);
s = "crack("+f1+","+(f2)+");"
eval(s);
}
function mousePressed(){
noLoop();
}
function crack(a,b){
strokeWeight(BASE/5);
stroke(0);
noFill();
push();
// print (a+":"+b);
translate(BASE*.137,BASE*.44);
beginShape();
push();
vertex(BASE*-3.392,BASE*-4.471);
vertex(BASE*-1.704,BASE*-3.673);
vertex(BASE*-1.367,BASE*-3.895);
vertex(BASE*-1.316,BASE*-3.14);
vertex(BASE*-0.497,BASE*-3.36);
vertex(BASE*0.084,BASE*-2.65);
//vertex(BASE*-0.894,BASE*-2.657);
vertex(BASE*-0.497,BASE*-3.36);
vertex(BASE*0.143,BASE*-3.791);
s = "vertex(BASE*"+a+",BASE*"+b+");";
// eval(s);
// print(s);
endShape();
pop();
noLoop();
saveCanvas("skull.png");
}
function mouth(){
strokeWeight(BASE/2.8);
stroke(0);
line(-BASE*4,BASE*2,BASE*4,BASE*2);
line(0,BASE*1.35,0,BASE*2.65);
line(-BASE,BASE*1.45,-BASE,BASE*2.55);
line(-2*BASE,BASE*1.4,-2*BASE,BASE*2.55);
line(BASE,BASE*1.45,BASE,BASE*2.65);
line(2*BASE,BASE*1.45,2*BASE,BASE*2.55);
}
function nose(a,b){
push();
push();
rotate(a);
ellipse(0,BASE/4,BASE*2,BASE);
pop();
push();
rotate(-a);
ellipse(0,+BASE/4,BASE*2,BASE);
pop();
}
function bigEye(f1,f2){
var g = 2.5;
push();
translate(f1* BASE,f2*BASE);
rotate(0.1);
ellipse(0,0,BASE*g,BASE*g*1.3);
pop();
}
function smallEye(f1,f2){
var g = 2.2;
push();
translate(f1* BASE,f2*BASE);
rotate(0.1);
ellipse(0,0,BASE*g*1.1,BASE*g);
pop();
}
It's tough enough to find programs with flood fill, actually.... it seems like almost every art program is either imitating "real" art tools, aimed at photo retouching, or tries to get you to think in their weird interface for vectors.
Anyway, I wanted a colored circle as a base for a Jack-O-Lantern tuba cover I'm making, and the easiest way was to go to the online p5 editor and enter the following:
function setup() {
createCanvas(3000, 3000);
noSmooth();
}
function draw() {
background(255);
noStroke();
fill(255,140,0);
ellipse(1500,1500,2500,2500);
saveCanvas("art.png");
noLoop();
}
That actually generates a file "art.png" which the browser will offer to download.
I then used Dropbox to transfer it to my iPad.
UPDATE: Did a bit more work...
So, I'm making up designs for tuba covers (as printed by the fine folks at http://stretchablecovers.net/) Before I mentioned the first step for a Jack-O-Lantern (drawn in by hand)
But a few years ago I had a DIY craft project (felt crudely stapled onto the blank side of a cover) skull for my tuba:
Net, net, you can see and play with the p5.js program here. The moderately clever bit is using a technique to let me finesse the placement of a piece via "map()"ing the X and Y mouse position to appropriate values, and then building a string for the corresponding ellipse() or line() command. I then both print the screen and run javascript's "eval()" on it. Once I get the fragement where I like it, I copy and paste the printed version of the statement and place it into the code, so it's kind of baked into the place and then I repeat the technique for the next fragment.
Oh, also for funsies check out my old Carve Your Own Jack-O-Lantern Processing Program... very similar in spirit to all this.
For reference (since I guess I shouldn't an account on a "alpha version" editor as a code repository) here is the code... it's not (quite) as hackish as it looks.
var BASE;
var s;
function setup() {
BASE = 30;// random(38,58);
createCanvas(BASE*10, BASE*10);
strokeCap(SQUARE);
}
function draw() {
//BASE += random(-.1,.1);
background(200);
noStroke();
translate(width/2,height/2);
fill(255);
ellipse(0,0,BASE*8,BASE*8);
fill(0);
push();
var f1 = map(mouseX,0,width, -5,5).toFixed(3);;
var f2 = map(mouseY,0,height, -5,5).toFixed(3);
var s;
//var s = "bigEye("+f1+","+f2+");";
s = "bigEye(1.835,-1.239);";
eval(s);
s = "nose(-1.867,0.824)";
eval(s);
//s = "smallEye("+f1+","+f2+")";
s = "smallEye(-2.037,-0.947)";
eval(s);
s = "mouth()";
eval(s);
s = "crack("+f1+","+(f2)+");"
eval(s);
}
function mousePressed(){
noLoop();
}
function crack(a,b){
strokeWeight(BASE/5);
stroke(0);
noFill();
push();
// print (a+":"+b);
translate(BASE*.137,BASE*.44);
beginShape();
push();
vertex(BASE*-3.392,BASE*-4.471);
vertex(BASE*-1.704,BASE*-3.673);
vertex(BASE*-1.367,BASE*-3.895);
vertex(BASE*-1.316,BASE*-3.14);
vertex(BASE*-0.497,BASE*-3.36);
vertex(BASE*0.084,BASE*-2.65);
//vertex(BASE*-0.894,BASE*-2.657);
vertex(BASE*-0.497,BASE*-3.36);
vertex(BASE*0.143,BASE*-3.791);
s = "vertex(BASE*"+a+",BASE*"+b+");";
// eval(s);
// print(s);
endShape();
pop();
noLoop();
saveCanvas("skull.png");
}
function mouth(){
strokeWeight(BASE/2.8);
stroke(0);
line(-BASE*4,BASE*2,BASE*4,BASE*2);
line(0,BASE*1.35,0,BASE*2.65);
line(-BASE,BASE*1.45,-BASE,BASE*2.55);
line(-2*BASE,BASE*1.4,-2*BASE,BASE*2.55);
line(BASE,BASE*1.45,BASE,BASE*2.65);
line(2*BASE,BASE*1.45,2*BASE,BASE*2.55);
}
function nose(a,b){
push();
push();
rotate(a);
ellipse(0,BASE/4,BASE*2,BASE);
pop();
push();
rotate(-a);
ellipse(0,+BASE/4,BASE*2,BASE);
pop();
}
function bigEye(f1,f2){
var g = 2.5;
push();
translate(f1* BASE,f2*BASE);
rotate(0.1);
ellipse(0,0,BASE*g,BASE*g*1.3);
pop();
}
function smallEye(f1,f2){
var g = 2.2;
push();
translate(f1* BASE,f2*BASE);
rotate(0.1);
ellipse(0,0,BASE*g*1.1,BASE*g);
pop();
}
Subscribe to:
Posts (Atom)