i'm probably going to roll this back but here's a change

This commit is contained in:
Shoofle 2022-10-22 14:14:09 -04:00
parent fc250eafbe
commit d8afa67553
296 changed files with 68490 additions and 0 deletions

View File

@ -0,0 +1,108 @@
"""Route requests for articles according to shoofle's rules.
This is a simple module which is basically entirely here to provide access, in a sensible location,
to the `bloop` object. It's a blueprint which describes how to route requests for articles on my website
(possibly located at http://shoofle.net, possibly not located there). Most of the interesting stuff is in
`render_article`; Go team!"""
import os
from flask import Blueprint, render_template, send_from_directory, abort
folder = "articles"
article_base_template = os.path.join(folder, "article.template.html")
article_small_template = os.path.join(folder, "article.zip.template.html")
bloop = Blueprint("articles", __name__, template_folder="")
@bloop.route("/")
def main_page():
"""Renders the list of articles/projects."""
return render_template("project_list.html")
@bloop.route("/.zip/")
def main_page_small():
return render_template("project_list.zip.html")
@bloop.route("/<article_name>/")
def render_article(article_name):
"""Renders a requested article! This should always be @routed last, because it catches a
wide variety of requests. As a result, other things need to be @routed first, because they
might never get called if this catches them first."""
# Arguably, the various options for how to render (templates, articles, flat html) could be stuck into various
# subdirectories. Ultimately I don't want to do this because I want this to be lightweight - this __init__.py file
# can be chucked into any folder and start showing pages correctly. But whatever!
# In the examples, let's think about a request for "example.com/some-article".
# First, we convert the important part of the requested page into a filename
# "example.com/Some-Article/" => folder="Some-Article" => file_name = "articles/some_article"
file_name = os.path.join(folder, article_name.replace("-", "_").lower())
# Here's the priority list for file rendering!
if os.path.isfile(file_name + ".template.html"):
# If the file "articles/some_article.template.html" exists, then there's a specific {template} written
# for this path. Specific page {templates} take priority.
return render_template(file_name + ".template.html")
if os.path.isfile(file_name + ".article.html"):
# If "articles/some_article.article.html" exists but there's no template, then we should render that
# {article fragment}, but using the {article base template}. In the future, this should possibly also
# extract the title from the {article fragment} and feed it into the {article base template} as well.
return render_template(article_base_template, target=file_name + ".article.html")
if file_name.endswith(".zip") and os.path.isfile(file_name[:-4] + ".article.html"):
return render_template(article_small_template, target=file_name[:-4] + ".article.html")
if os.path.isfile(file_name + ".html"):
# If we haven't found any results yet, check to see if "articles/some_article.html" exists. If it does,
# just display it plain. This also provides a clean way to access the raw form of files like
# "articles/some_article.template.html" - just make a request for "example.com/some-article.template/"
# and it will be caught by this rule and rendered.
return render_template(file_name + ".html")
if os.path.isfile(file_name):
# If it didn't match any other rules, then just render the file that has precisely the requested name.
return render_template(file_name);
# I *believe* there's one instance that can't be accessed by this kind of routing in any way:
# If the files "articles/some_article" and "articles/some_article.html" both exist, then no request will
# convince this blueprint to return the former. However, as sacrifices go, I don't think it's too bad, and
# that should be the only case when this happens.
# It also can't find files with hyphens in their name, because they get replaced with underscores.
# If we didn't find any files, throw up a 404.
abort(404)
@bloop.route("/<article_name>/<path:file_path>")
def supplementary(article_name, file_path):
"""Sends a file such that articles can have supplementary content.
The article at "example.com/great-articles" will have its article fragment HTML defined at
"articles/great_articles.article.html" and its supplementary content will be found in the folder
"articles/great_articles/some_image.jpg".
Oh, and one last thought - if we want more complicated supplementary content behaviors, it might be
necessary to make a blueprint for it. Then, it's necessary to include the blueprint in this file. Oh well.
"""
# Put the article name into the standard form, and concatenate them together.
article_name = article_name.replace("-", "_").lower()
# You could make a strong argument that this is unnecessary, and we could just shove in a static file handler.
# But I like this solution more. It better separates out the supplementary content from the article itself.
# Things that don't fit into this framework might not belong as articles, anyway!
# Important! This didn't work right for a long time until I finally made it join `folder` to the beginning.
# Without that, it tries to load the file relative to wherever the server's running - which is /not/ the
# right thing. The server's running somewhere, but this needs to be in the articles folder, yadda yadda,
# you can figure it out.
path = os.path.join(folder, article_name, file_path)
# If the path exists and is a file, then we should send it.
if os.path.isfile(path):
directory, file_name = os.path.split(path)
print("sending %(fn)s from directory %(d)s" % {"fn": file_name, "d": directory})
return send_from_directory(directory, file_name)
# If that file wasn't found, then, well, whoops.
abort(404)
bloop.route("/<article_name>.zip/<path:file_path>")(supplementary)
"""Th-th-th-that's all, folks!"""

View File

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
{% block head %}
<meta charset="utf-8">
<title>{% block title %}Writings by Shoofle{% endblock %}</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
{% endblock %}
</head>
<body>
{% block body -%}
{%- include target -%}
{%- endblock %}
</body>
</html>

View File

@ -0,0 +1,21 @@
<!doctype html>
<html>
<head>
{% block head %}
<meta charset="utf-8">
<title>{% block title %}Writings by Shoofle{% endblock %}</title>
<script src="/static/jquery-2.0.3.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
<style type="text/css">
body { transform: scale(0.5); transform-origin: 0% 0% }
</style>
{% endblock %}
</head>
<body>
{% block body -%}
{%- include target -%}
{%- endblock %}
</body>
</html>

View File

@ -0,0 +1,308 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<style type="text/css">
#server-choices colgroup { width: 20%; }
#server-choices tr { height: 3em; }
#server-choices tr>td { text-align: center; }
#server-choices tr>td:first-child {
text-align: left;
font-weight: bold;
}
</style>
<article>
<h1>Shit, I better make this server work.</h1>
<h3>On the layout of folders and files!</h3>
<p>Basically I'm not sure how I should lay out this website. The current organization looks something like this:</p>
<ul class="file-tree">
<li><i class="icon-folder-open"></i> <span class="filename">server/</span> (server root)
<ul>
<li><i class="icon-align-left"></i> <span class="filename">main_page.html</span></li>
<li><i class="icon-folder-open"></i> <span class="filename">pages/</span> (articles with nice links)
<ul>
<li><i class="icon-align-left"></i> <span class="filename">auriga.html</span> (accessed as <span class="url">http://example.com/auriga/</span>)</li>
<li><i class="icon-align-left"></i> <span class="filename">language_for_games.html</span> (<span class="url">http://example.com/language-for-games/</span>)</li>
<li><i class="icon-align-left"></i> <span class="filename">tamari.html</span> (<span class="url">http://example.com/tamari/</span>)
</ul>
</li>
<li><i class="icon-folder-open"></i> <span class="filename">static/</span>
<ul>
<li><i class="icon-eye-open"></i> <span class="filename">stylesheet.css</span> (global stying)</li>
<li><i class="icon-picture"></i> <span class="filename">favicon.ico</span></li>
<li><i class="icon-picture"></i> <span class="filename">banner.png</span></li>
<li><i class="icon-folder-open"></i> <span class="filename">tamari/</span> (files for an article)
<ul>
<li><i class="icon-picture"></i> <span class="filename">figure_1.png</span></li>
<li><i class="icon-picture"></i> <span class="filename">figure_2.png</span></li>
<li><i class="icon-signal"></i> <span class="filename">experiment_data.csv</span></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>This has a number of problems. First and foremost, barely any thought has gone into naming of folders and files - which is because not much thought has gone into their placement and hierarchy.</p>
<p>The server is written using <a href="http://flask.pocoo.org">Flask</a>, and this is the rundown on how it works when a user goes to <span class="url">http://example.com/some-path/</span>:</p>
<ol>
<li>If the path requested is the root of the website, render <span class="filename">main_page.html</span>.</li>
<li>If the path requested begins with <span class="url">static/</span>, render a file from the static content directory.</li>
<li>Else, convert the path (<span class="url">some-path</span>) into the filename (<span class="filename">pages/some_path.html</span>) assuming it's an article. If it exists, render it.</li>
<li>If there's no article by that name, display the miscellaneous articles page. Or 404. The current behavior isn't very high on my priorities.</li>
</ol>
<p>There are a bunch of problems with this system - it's kind of brittle, file paths have the meaningless-to-the-user <span class="url">static/</span> at the beginning, and it means that when I include a resource in <span class="url">http://example.com/tamari/</span> (the file <span class="filename">pages/tamari.html</span>) I have to use a path like <span class="url">../static/tamari/figure_1.png</span> - when I should be able to reference files related to the Tamari article by a closely-related URL: <span class="url">./figure_1.png</span>.</p>
<p>All this points to the idea of having my articles be in folders, so it would look something like this:</p>
<ul class="file-tree">
<li><i class="icon-folder-open"></i> <span class="filename">server/</span> (server root)
<ul>
<li><i class="icon-align-left"></i> <span class="filename">main_page.html</span></li>
<li><i class="icon-folder-open"></i> <span class="filename">articles/</span> (articles with nice links)
<ul>
<li><i class="icon-folder-open"></i> <span class="filename">auriga/</span>
<ul>
<li><i class="icon-align-left"></i> <span class="filename">auriga.html</span></li>
</ul>
</li>
<li><i class="icon-folder-open"></i> <span class="filename">language-for-games/</span>
<ul>
<li><i class="icon-align-left"></i> <span class="filename">language_for_games.html</span></li>
</ul>
</li>
<li><i class="icon-folder-open"></i> <span class="filename">tamari/</span>
<ul>
<li><i class="icon-align-left"></i> <span class="filename">tamari.html</span></li>
<li><i class="icon-picture"></i> <span class="filename">figure_1.png</span></li>
<li><i class="icon-picture"></i> <span class="filename">figure_2.png</span></li>
<li><i class="icon-signal"></i> <span class="filename">experiment_data.csv</span></li>
</ul>
</li>
</ul>
</li>
<li><i class="icon-folder-open"></i> <span class="filename">static/</span>
<ul>
<li><i class="icon-eye-open"></i> <span class="filename">stylesheet.css</span> (global stying)</li>
<li><i class="icon-picture"></i> <span class="filename">favicon.ico</span></li>
<li><i class="icon-picture"></i> <span class="filename">banner.png</span></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>And hell, at this point having the filenames match the folders (<span class="filename">auriga/auriga.html</span>) is just a pain, so we could name the main page in each folder <span class="filename">index.html</span>, and suddenly this looks very standard. So why don't I do this?</p>
<p>Well... I don't know. But something always bothered me about the <span class="filename">index.html</span> standard. Maybe it's just that my article about Tamari lattices <em>isn't</em> an index, so I don't think it should be called one. That's certainly part of it. Also, some of my articles and pages are going to be very short - in theory I'd like them to be able to be tiny snippets, which could be included on other pages. It'd be annoying to have a subfolder for every single article I write! But... maybe not <em>too</em> annoying. I'll have to think about that.</p>
<hr>
<h3>But what are we <em>doing</em>?</h3>
<p>What would my ideal web framework be? Well, something like Flask, really. I like flask because this is easy:</p>
<pre>
<code class="language-python">@app.route("<span class="url">/some-convoluted/url/&lt;options&gt;/with-stuff/</span>")
def render_shoofle_page(options=None):
if options == "bananas":
return render_page("<span class="filename">bananas.html</span>")
else:
return render_page("<span class="filename">some_other_template.html</span>", options=options)</code></pre>
<p>And, lo and behold, I can go to <span class="url">example.com/some-convoluted/url/bananas/with-stuff/</span> and see the right generated page! I like having that capacity at my fingertips. In particular, it makes it easy to respond to various other kinds of requests. That's what I really like about Flask - I feel like I can do <em>everything</em> a webserver can do, but it all takes the same very small amount of work. This compares to, say, PHP, where it's easy to make pages... But more complicated behavior, especially anything involving dynamic URL paths, involves jumping through hoops.<span class="sidenote span4">It might seem like an overblown complaint, but the integrity of my mental models is important to me - and in PHP, I don't understand what gets sent to the requestor. If I make a Flask route returning JSON, I know it's exactly what a script wants. With PHP, I would <em>hope</em> that it wouldn't have weird headers or content type or additional markup.</span> Plus, you have to work with PHP, which I would wish on no one.</p>
<p>I don't like Flask because I need to specify all the behaviors, and I'm worried that I'll get it wrong (thus, this article).</p>
<p>My ideal world solution would be something that lets me pass requests for subpaths to other python modules:<p>
<ul class="file-tree">
<li><i class="icon-folder-open"></i> <span class="filename">server/</span>
<ul>
<li><i class="icon-file"></i> <span class="filename">server.py</span></li>
<li><i class="icon-align-left"></i> <span class="filename">main_page.html</span></li>
<li><i class="icon-folder-open"></i> <span class="filename">articles/</span>
<ul>
<li><i class="icon-file"></i> <span class="filename">articles.py</span></li>
<li><i class="icon-align-left"></i> <span class="filename">auriga.html</span></li>
<li><i class="icon-align-left"></i> <span class="filename">language-for-games.html</span></li>
<li><i class="icon-align-left"></i> <span class="filename">tamari.html</span></li>
</ul>
</li>
<li><i class="icon-folder-open"></i> <span class="filename">games/</span>
<ul>
<li><i class="icon-file"></i> <span class="filename">games.py</span></li>
<li><i class="icon-signal"></i> <span class="filename">scores.csv</span></li>
<li><i class="icon-align-left"></i> <span class="filename">play_the_game.html</span></li>
<li><i class="icon-align-left"></i> <span class="filename">game.js</span></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Aaaand how it works would be simple:</p>
<ol>
<li>A request comes in to <span class="url">http://example.com/tamari/</span>, and it is handled by <span class="filename">server.py</span>.</li>
<li><span class="filename">server.py</span> says "Okay, I don't have any specific handlers that do anything with this URL, but I think <span class="filename">articles/articles.py</span> does!" and sends the request to <span class="filename">articles.py</span>.</li>
<li><span class="filename">articles.py</span> receives a request for <span class="url">/tamari/</span> and has a rule for handling that, and so it renders the appropriate HTML file.</li>
</ol>
<p>The basic idea is that requests can be routed to other python modules. If those modules can be reloaded easily (or automatically!), this would mean I could write complicated URL handling in subdirectories of my website - without having to touch any of the code that handles anything else. It means I can modularize - and that's <em>awesome</em>.</p>
<ol>
<li>An HTTP GET request comes in to <span class="url">http://example.com/games/points</span>, and <span class="filename">server.py</span> takes it.</li>
<li><span class="filename">server.py</span> doesn't know what to do with it, but it knows that requests for URLs starting with <span class="url">/games</span> should be handled by <span class="filename">games/games.py</span>.</li>
<li><span class="filename">games.py</span> receives an HTTP GET request for <span class="url">/games/points</span>, and so it can respond to this with a JSON object that tells the requestor how many points they got.</li>
</ol>
<p>This is the main point of the tiered handlers system - that I can write handlers for all kinds of requests, without having to interact with other parts of my website.</p>
<p>Basically, I want to combine the simple directory structure of PHP websites with the principles of Flask I really like - separating content from server logic, keeping things simple, and having control.</p>
<p>Here's how I imagine this would work:</p>
<pre>
<code>from flask import Flask, render_template, redirect
app = Flask(__name__)
def main_page(): return render_template("<span class="filename">main.html</span>")
app.add_url_rule("<span class="url">/</span>", main_page)
import games
app.add_module_url_rule("<span class="url">/games/&lt;path:rest&gt;</span>", games)</code></pre>
<p>As usual, don't think too hard about the syntax I just proposed. The point is to distribute URL routing logic throughout multiple files, so that my entire website isn't controlled by one brutalistic overlord <span class="filename">server.py</span> file that decrees the entire path of every request. I want things split up, dammit!</p>
<hr>
<p>Now I'm reading up on the Flask docs, and it turns out that it already has the capacity for this. Unfortunately, it's complicated and I'm <em>whiiiny</em>.</p>
<p>It seems that Flask provides the developer with three options:</p>
<ul>
<li>Package Organization: Essentially, break up your server code, like configuration and URL route assignment, into separate files. This doesn't actually solve any of my issues - most of what this does (as far as I can tell) is separate the server initialization logic from the routing and response logic. Plus, it makes it easier to use your application in something else. More on this soon.</li>
<li>Blueprints: Very close to what I want. I could define a Blueprint object in <span class="filename">articles.py</span>. This Blueprint object would register all the routes and endpoints that I want to register - and then, when the server starts, it gets all of those from the blueprint. This means we can <em>actually</em> factor views out. I'm really not sure, though, what this affords that the organizing your application as a package doesn't.</li>
<li>Application Dispatching: The most powerful of the three! This is a slightly lower-level solution, wherein you actually run multiple applications concurrently, and dispatch requests to them. This is kind of overkill, but at least it's nice to know that it's possible - things like the <span class="url">async_app</span> example from previous could really make sense, if the app is large enough in scope. Application dispatching means you can just pull in another application and route things to it on the WSGI layer. Or something.</li>
</ul>
<p>Okay, but... how do they score up? I have some requirements:</p>
<dl>
<dt>Organization</dt>
<dd>I want to define the behavior for <span class="url">example.com/articles/tamari/</span> in the same place as <span class="url">example.com/articles/language-for-games/</span> is defined, but in a different file from where <span class="url">example.com/games/hunt-the-wumpus</span> is defined.</dd>
<dt>Simplicity</dt>
<dd>Defining a URL route and endpoint for each subfolder of my application shouldn't be much more complicated than defining behaviors in a flat system. Ideally, registering all the behaviors for the <span class="url">articles/</span> subpath is no more complicated than <code>app.route("<span class="url">/articles/</span>", articles_handler)</code></dd>
<dt>Ease of Extension</dt>
<dd>I'm writing a family of pages. They're starting to get a little too complicated. It should be really easy to go from that situation to having a nicely-delineated subsection of my website.</dd>
<dt>Do I <em>Need</em> to Restart the Server?</dt>
<dd>I hate restarting the server. Maybe this is a little thing, but gah! I want the server to detect changes automatically! This shouldn't be that hard. Of course, this is a much thornier topic when it comes to subpages that might store their own state... So it might be out the window unless I want to get my hands <em>way</em> more dirty.</dd>
</dl>
<div class="container-fluid">
<div class="row-fluid">
<table id="server-choices" class="span8 offset2">
<caption><h4>How do they fare up?</h4></caption>
<col><colgroup span=4></colgroup>
<thead>
<tr>
<th></th>
<th>Organization</th>
<th>Simplicity</th>
<th>Ease of Extension</th>
<th>No Restart Required?</th>
</thead>
<tbody>
<tr>
<td>Current</td>
<td class="label-warning"><i class="icon-question-sign"></i></td>
<td class="label-success"><i class="icon-ok-sign"></i></td>
<td class="label-important"><i class="icon-remove-sign"></i></td>
<td class="label-success"><i class="icon-ok-sign"></i></td>
</tr>
<tr>
<td>Organize as a Package</td>
<td class="label-success"><i class="icon-ok-sign"></i></td>
<td class="label-warning"><i class="icon-question-sign"></i></td>
<td class="label-warning"><i class="icon-question-sign"></i></td>
<td class="label-important"><i class="icon-remove-sign"></i></td>
</tr>
<tr>
<td>Blueprints</td>
<td class="label-success"><i class="icon-ok-sign"></i></td>
<td class="label-success"><i class="icon-ok-sign"></i></td>
<td class="label-success"><i class="icon-ok-sign"></i></td>
<td class="label-important"><i class="icon-remove-sign"></i></td>
</tr>
<tr>
<td>Application Dispatch</td>
<td class="label-success"><i class="icon-ok-sign"></i></td>
<td class="label-important"><i class="icon-remove-sign"></i></td>
<td class="label-important"><i class="icon-remove-sign"></i></td>
<td class="label-important"><i class="icon-remove-sign"></i></td>
</tr>
</tbody>
</table>
</div></div>
<p>In light of this chart, it seems like Blueprints are the clear option. Also, "organize as package" is really vague. What they suggested didn't sound very helpful - but making python packages (finally learning how to use <span class="filename">__init__.py</span> files!) will probably get used in the solution I'm thinking I'll use, which looks something like this:</p>
<ul class="file-tree">
<li><i class="icon-folder-open"></i> <span class="filename">server/</span>
<ul>
<li><i class="icon-file"></i> <span class="filename">server.py</span></li>
<li><i class="icon-folder-open"></i> <span class="filename">articles/</span>
<ul>
<li><i class="icon-file"></i> <span class="filename">article_routing_blueprint.py</span></li>
<li><i class="icon-align-left"></i> <span class="filename">main_page.html</span></li>
<li><i class="icon-align-left"></i> <span class="filename">auriga.html</span></li>
<li><i class="icon-align-left"></i> <span class="filename">language-for-games.html</span></li>
<li><i class="icon-align-left"></i> <span class="filename">tamari.html</span></li>
<li><i class="icon-folder-close"></i> <span class="filename">tamari_files</span></li>
</ul>
</li>
<li><i class="icon-folder-open"></i> <span class="filename">games/</span>
<ul>
<li><i class="icon-file"></i> <span class="filename">games_blueprint.py</span></li>
<li><i class="icon-signal"></i> <span class="filename">scores.csv</span></li>
<li><i class="icon-align-left"></i> <span class="filename">play_the_game.html</span></li>
<li><i class="icon-align-left"></i> <span class="filename">game.js</span></li>
</ul>
</li>
<li><i class="icon-folder-open"></i> <span class="filename">static/</span>
<ul>
<li><i class="icon-picture"></i> <span class="filename">favicon.ico</span></li>
<li><i class="icon-eye-open"></i> <span class="filename">stylesheet.css</span></li>
<li><i class="icon-picture"></i> <span class="filename">lol_a_cat.jpg</span></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>If there's static content that is globally important, with simple routing rules, it can - of course - be stored in the server root's <span class="filename">static/</span> directory. Static file handlers don't take much configuration, so this can go in the root.</p>
<p>All the files related to articles are contained in the <span class="filename">articles/</span> directory, or subdirectories. <span class="filename">article_routing_blueprint.py</span> contains a blueprint definition that tells the server how to route calls that result in the rendering of the various articles I've written.</p>
<p>All files related to the little javascript games I've made, on the other hand, go in the <span class="filename">games/</span> directory. State for this application is managed by <span class="filename">games_blueprint.py</span>, which is sort of like a very tiny application. I think it's possible to keep track of state, if you remember to keep variables global... This is tricksy, but should be doable - and if it gets larger, it should almost certainly be developed as a separate application entirely. At that point, I should figure out how to include multiple applications in one website!</p>
<p>I want to finish this bit by writing down some code for the base <span class="filename">server.py</span> logic:</p>
<pre>
<code class="language-python">from flask import Flask
# app configuration and initialization
app = Flask(__name__)
app.debug = True
# content in <span class="filename">articles/</span>
import articles
app.register_blueprint(articles.blueprint)
# content in <span class="filename">games/</span>
import games
app.register_blueprint(games.blueprint, url_prefix="<span class="url">/games</span>")</code></pre>
<hr>
<h3>Okay, one last thing.</h3>
<p>This has been a lot about how to get some basic extensible functionality working. <i class="that-guy">But wait</i>, someone cries in the distance! <i class="that-guy">Why are we going to such lengths to do all this in the first place, when you're just serving static HTML files? Isn't this way more work than you need?</i><span class="sidenote span4">In case you're reading this article long after I've finished these changes (I hope so, 'cause I should get to work on this immediately), at this point all the articles were static HTML files. That voice speaks the truth.</span></p>
<p>Thanks for asking. The answer is that I want to change how this site is structured, and the articles are the specific thing I want to change. See, as it is, each article has basically the same header at the top, and basically the same footer at the bottom, and... Well, they're just all enclosed in the same stuff, and so when I change the styling of the website - or, horror of horrors, try to add a sidebar across the hwole site - I have to change all of them! And, as I think we've been over before, I'm <em>lazy</em>.</p>
<p>Conveniently, as I've been obsessively reading discussions of standards-compliant HTML5 and document formats and such lately, this laziness messes with my indomitable enthusiasm for conceptually pleasing solutions. Here's what I've got:</p>
<ul>
<li><b>Articles</b> on my site are blocks of text, consisting of my rambling on some topic. Something I've written down. They can have some amount of associated content, but for the most part they're just text. So, I'm going to reduce them down to their bare minimum of unique content - in many cases, just an <code language="html">&lt;article&gt;</code> tag containing what I wrote!</li>
<li>But sometimes I want to have multiple articles display in one page! Sometimes my articles are single paragraphs. Sometimes they can be grouped into related, uh, groups! What do we do then? The answer is <strong>templates</strong>. Flask uses <a href="http://jinja.pocoo.org/">Jinja2</a>, a perfectly nice templating engine - and why not use it?</li>
<li>If I want to display the articles contained in <span class="filename">articles/parkour_is_awesome.html</span>, <span class="filename">articles/exercise.html</span>, and <span class="filename">articles/how_is_body.html</span>), then I just make a new page, a Jinja2 template, which contains this:
<pre>
<code class="language-jinja2"><!-- -->{% include "<span class="filename">articles/parkour_is_awesome.html</span>" %}
{% include "<span class="filename">articles/exercise.html</span>" %}
{% include "<span class="filename">articles/how_is_body.html</span>" %}<!-- --></code></pre>
</li>
</ul>
<p>And ta-da! We now have a page which has those three articles in a row.</p>
<p>This, of course, also means we have a neat format for how to display things in general. Someone requested access to a URL! Find the file it points to. If it's an HTML fragment, then point the <b>general page template</b> to that file, and render. The general page template is just a template that adds the header, footer, sidebar, universal styling, and so on, as required, for universality.</p>
<p>So if you go to <span class="url">example.com/tamari/</span>, the server says "Okay, you want to look at the article in the HTML fragment file <span class="filename">articles/tamari.html</span>." It renders the general page template around that article, and returns that to the user.</p>
<p>Because I like having the option to look at these things many ways, I'm probably also going to allow for a user to do <code class="language-bash">python articles.py --generate [path]</code>, which will simply spit out the the rendered results of going to <code class="language-bash">[path]</code>. I guess that <em>might</em> just be indistinguishable from using <code class="language-bash">wget</code>, but hey. It can't hurt to give more functionality!</p>
<p>It remains to be seen precisely how many pages on this site will use the general page template. I don't want to rely on it too much, but, man, is there <em>any</em> reason not to? Basically, it means that I don't have to worry about writing the headers of anything on my website. I guess I'll need to be able to throw it off when I'm making subpages that are intended to be stylistically distinct from the rest of my site.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,270 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<style type="text/css">
#consonant-grid, #vowel-grid {
margin-left: auto;
margin-right: auto;
}
td {
padding: 2em;
}
.vowel-example {
padding: 1em;
}
td, .vowel-sequence {
border: 2px solid gray;
}
table > img {
width: 50%;
height: 50%;
}
.hero-example {
width: 50%;
display: block;
margin-left: auto;
margin-right: auto;
}
.example-glyph {
width: 20%;
display: block;
margin-left: auto;
margin-right: auto;
}
#vowel-grid {
display: block;
width: 60%;
}
.vowel-sequence {
display: block;
}
.vowel-example {
display: inline-block;
}
.vowel-example * {
vertical-align: middle;
}
.vowel-example > svg {
width: 3.5em;
height: 3.5em;
}
.unverified {
color: lightgray;
}
</style>
<h3>An introduction to Circle Script (Version... 3?)</h3>
<p>Circle script is a writing system that I made up. The fundamental gimmick of it is that words and sentences in it form circles.</p>
<img src="splash-example.svg" alt="A large glyph composed of nested circles." class="hero-example" />
<p>It's largely decorative, has vague intentions at being phonetic, and I like using it in art! The other primary use case is writing cute notes to people I like.</p>
<h4>Here's the overview of how to read circle script:</h5>
<p>To read circle script, you start at whatever spot is marked with a little line segment outside the circle, parallel to it. If that's not present, you can usually start at the bottom. Walk around the circle widdershins (counterclockwise) and decode each shape and glyph you encounter, turning them into sounds.</p>
<img src="/circle_script/zagreus.svg" alt="A circle script glyph showing a single word-circle, with a small line outside the circle for a starting-point indicator." class="example-glyph"/>
<p>If you're reading a whole sentence, you walk around the circle and read each word as you come across it. For each word, you read it starting from the point where it touches the circle that contains it, so you kind of reorient as you go.</p>
<p>Also, you might have noticed that some of the shapes inside word-circles have lines connecting them. Those are decorative! A lot of the component parts of the circle script writing system have places for lines to come off. These can be connected as you like, but the... proper? I guess? fancy, perhaps? way to use them is to draw connecting lines between words which are conceptually linked. You might use those lines to connect an adjective with the noun it modifies, or the names of two people who care about each other. There's a lot of room for poetic embellishment in drawing lines between words!</p>
<p>Of course, you can also just draw the lines out in a way that looks cool. :)</p>
<h4>Okay, now the meat of circle script: How to read words!</h5>
<p>To read circle script, you need to understand consonants and vowels. Consonants are easier, so we'll start with those. Consonants are drawn with marks that float above (inside) the continuous line of the circle. Each consonant is composed of a shape, which corresponds to what kind of sound it is, and a diacritic, which indicates where in the mouth it is made. Additionally, if the consonant is <a href="https://en.wikipedia.org/wiki/Voice_(phonetics)">voiced</a>, the shape will generally be drawn with a doubled line.</p>
<p><strong>Phew!</strong> That sounded complicated. It's not so bad, really - I'm going to give you a grid, and you can just find the sound you want, and it'll tell you how to draw it. All of that business about kinds of sounds, places in mouths, and voice just means this is a featural script, so that similar sounds are indicated by similar-looking letters.</p>
<aside><p>In this version of circle script, consonants do not distort the overall shape, and vowels do. In versions 1 and 2, it was the other way around!</p></aside>
<table id="consonant-grid">
<thead>
<tr class="header">
<th></th>
<th scope="col" class="diacritic-header"><img src="example-diacritic-1.svg" /></th>
<th scope="col" class="diacritic-header"><img src="example-diacritic-2.svg" /></th>
<th scope="col" class="diacritic-header"><img src="example-diacritic-3.svg" /></th>
<th scope="col" class="diacritic-header"><img src="example-diacritic-4.svg" /></th>
<th scope="col" class="diacritic-header"><img src="example-diacritic-5.svg" /></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" class="consonant-shape"><img src="consonant-mound-1.svg" /></th>
<td>[m] <strong>m</strong>ap</td>
<td>[n] <strong>n</strong>ope</td>
<td class="unverified">[ñ] pi<strong>ñ</strong>ata (unverified)</td>
<td>[ŋ] ha<strong>ng</strong></td>
<td></td>
<th scope="row" class="consonant-shape"><img src="consonant-mound-2.svg" /></th>
</tr>
<tr>
<th scope="row" class="consonant-shape"><img src="consonant-ohm-1.svg" /></th>
<td>[p] <strong>p</strong>ear, [b] <strong>b</strong>at</td>
<td>[t] <strong>t</strong>alk, [d] <strong>d</strong>eal</td>
<td></td>
<td>[k] <strong>k</strong>ale, [g] <strong>g</strong>ulp</td>
<td class="unverified">[q], [ɢ] (unverified)</td>
<th scope="row" class="consonant-shape"><img src="consonant-ohm-2.svg" /></th>
</tr>
<tr>
<th scope="row" class="consonant-shape"><img src="consonant-angle-1.svg" /></th>
<td>[f] <strong>f</strong>ace, [v] <strong>v</strong>eer</td>
<td>[s] <strong>s</strong>ight, [z] <strong>z</strong>oot</td>
<td>[ʃ] <strong>sh</strong>ort, [ʒ] mea<strong>s</strong>ure</td>
<td></td>
<td class="unverified">[ʔ], [ʡ] (unverified)</td>
<th scope="row" class="consonant-shape"><img src="consonant-angle-2.svg" /></th>
</tr>
<tr>
<th scope="row" class="consonant-shape"><img src="consonant-loop-1.svg" /></th>
<td>[θ] <strong>th</strong>in, [ð] <strong>th</strong>at</td>
<td class="unverified">[ts] <strong>ts</strong>ar? (unverified)</td>
<td>[tʃ] <strong>ch</strong>alk, [dʒ] <strong>j</strong>u<strong>dg</strong>e</td>
<td>[x~χ] lo<strong>ch</strong>, <span class="unverified">[ʁ] french r</span></td>
<td></td>
<th scope="row" class="consonant-shape"><img src="consonant-loop-2.svg" /></th>
</tr>
<tr>
<th scope="row" class="consonant-shape"><img src="consonant-wave-1.svg" /></th>
<td>[w] <strong>w</strong>alk</td>
<td>[ɹ] <strong>r</strong>eal, [l] <strong>l</strong>atent</td>
<td>[ɾ~r] pe<strong>rr</strong>o, <span class="unverified">[ɬ] welsh ll</span></td>
<td>[j] <strong>y</strong>eet</td>
<td>[h] <strong>h</strong>edonism</td>
<th scope="row" class="consonant-shape"><img src="consonant-wave-2.svg" /></th>
</tr>
</tbody>
</table>
<p>If you're familiar with the <a href="https://en.wikipedia.org/wiki/International_Phonetic_Alphabet">international phonetic alphabet</a> this might look somewhat familiar! (Although I've taken some heavy liberties to make this fit into a grid and match with my American understanding of what sounds are similar) Each square in the grid shows the two consonants (one unvoiced, one unvoiced) indicated by that combination of diacritic (above) and shape (from the side). The unvoiced version (on the left, in a grid square) is indicated by the single-line version of the shape, seen on the left side of the grid. The voiced version uses the "heavy" version, seen on the right side of the grid and originally drawn with a doubled line. I've included the IPA transcription for the consonants, along with an example of that sound in my dialect, which is probably roughly <a href="https://en.wikipedia.org/wiki/General_American_English">General American English</a>.</p>
<p>Vowels are a little more haphazard. Have you ever really thought about vowels? They're pretty messed up, especially in English. We were taught the five vowels are a, e, i, o, u, and sometimes y. It turns out that while we have five and a half <em>letters</em> we call vowels, in English, they correspond to anywhere between twelve and fifteen different <em>sounds</em>. And those sounds aren't even necessarily the ones you're thinking of - a lot of the things we think of as individual vowels, like the /i/ in "r<strong>i</strong>ght", are actually diphthongs, composed of two vowels that we run together.</p>
<p>Anyway, circle script draws vowels as modifications to the outer circle of a word - as you're drawing the circle, you take a detour to take either a small bite out of it, a big bite out of it, or a loop-the-loop. It is then decorated with a circle, placed either above the detour shape (on the inside of the overall word circle), below the detour shape (often inside the detour shape, towards the outside of the word circle), or on the line of the detour circle. Then, you add a line for emphasis if necessary.</p>
<p>The vowels are organized in a way close to the five vowel system seen in many languages. Consider the Japanese ka ki ku ke ko; these are considered the "strong" versions of the five basic vowels. They also come in "weak" versions, common in English. The pairing between strong and weak versions is roughly based on whatever I thought made the most sense to my ears. They're listed in the vowel chart. A strong vowel will have a line extending off it, which can be used freely as with the connectors on the consonant glyphs, and a weak vowel will have no such line.</p>
<p>There's one vowel that I didn't quite fit into a correspondence with another "stronger" or "weaker" vowel, /æ/, which you might know from the word "bat" or "back" (in American English). There are also three common diphthongs provided as vowel glyphs. </p>
<p>Hopefully, if this system needs to be extended for other vowels, they can be defined by relation to a "similar" sounding vowel, and adding a diacritic of some sort. But that hasn't been standardized yet.</p>
<p>The examples I give are based on my accent (some variation of general American English), so if there's any confusion, consult a resource like <a href="https://ipachart.com">ipachart.com</a> for recordings of vowels. Several common diphthongs are available as vowel shapes.</p>
<object data="vowel-example.svg" type="image/svg+xml" style="display: none"></object>
<div id="vowel-grid">
<div class="vowel-sequence">
<div class="vowel-example">
<svg viewBox="0 0 24 80" >
<use href="vowel-example.svg#base-omega" ></use>
<use href="vowel-example.svg#dot-outer"></use>
<use href="vowel-example.svg#emphasis-outer"></use>
</svg>
[ɑ, a], box or bah
</div>
<div class="vowel-example">
<svg viewBox="0 0 24 80" >
<use href="vowel-example.svg#base-omega" ></use>
<use href="vowel-example.svg#dot-outer"></use>
</svg>
[ʌ, ə], but or schwa (unstressed)
</div>
</div>
<div class="vowel-sequence">
<div class="vowel-example">
<svg viewBox="0 -12 24 80" >
<use href="vowel-example.svg#base-mound" ></use>
<use href="vowel-example.svg#dot-inner"></use>
<use href="vowel-example.svg#emphasis-inner"></use>
</svg>
[i], beat
</div>
<div class="vowel-example">
<svg viewBox="0 -12 24 80" >
<use href="vowel-example.svg#base-mound" ></use>
<use href="vowel-example.svg#dot-inner"></use>
</svg>
[ɪ], bit
</div>
</div>
<div class="vowel-sequence">
<div class="vowel-example">
<svg viewBox="0 -12 24 80" >
<use href="vowel-example.svg#base-mound" ></use>
<use href="vowel-example.svg#triangle-inner"></use>
<use href="vowel-example.svg#emphasis-inner"></use>
</svg>
[u], boot
</div>
<div class="vowel-example">
<svg viewBox="0 -12 24 80" >
<use href="vowel-example.svg#base-mound" ></use>
<use href="vowel-example.svg#triangle-inner"></use>
</svg>
[ʊ], book
</div>
</div>
<div class="vowel-sequence">
<div class="vowel-example">
<svg viewBox="0 -12 24 80" >
<use href="vowel-example.svg#base-mound" ></use>
<use href="vowel-example.svg#dot-outer"></use>
<use href="vowel-example.svg#emphasis-outer"></use>
</svg>
[ɛi, e], bait
</div>
<div class="vowel-example">
<svg viewBox="0 -12 24 80" >
<use href="vowel-example.svg#base-mound" ></use>
<use href="vowel-example.svg#dot-outer"></use>
</svg>
[ɛ], bet
</div>
</div>
<div class="vowel-sequence">
<div class="vowel-example">
<svg viewBox="0 0 24 80" >
<use href="vowel-example.svg#base-omega" ></use>
<use href="vowel-example.svg#dot-inner"></use>
<use href="vowel-example.svg#emphasis-inner"></use>
</svg>
[o, ɔu], won't, boat
</div>
<div class="vowel-example">
<svg viewBox="0 0 24 80" >
<use href="vowel-example.svg#base-omega" ></use>
<use href="vowel-example.svg#dot-inner"></use>
</svg>
[ɔ], bawk
</div>
</div>
<div class="vowel-sequence">
<div class="vowel-example">
<svg viewBox="0 0 24 80" >
<use href="vowel-example.svg#base-omega" ></use>
<use href="vowel-example.svg#dot-on"></use>
</svg>
[æ], bat
</div>
</div>
<div class="vowel-sequence">
<div class="vowel-example">
<svg viewBox="0 0 24 80" >
<use href="vowel-example.svg#base-loop" ></use>
<use href="vowel-example.svg#dot-outer"></use>
</svg>
[au], cow
</div>
<div class="vowel-example">
<svg viewBox="0 0 24 80" >
<use href="vowel-example.svg#base-loop" ></use>
<use href="vowel-example.svg#dot-on"></use>
</svg>
[ai, aɪ], by
</div>
<div class="vowel-example">
<svg viewBox="0 0 24 80" >
<use href="vowel-example.svg#base-loop" ></use>
<use href="vowel-example.svg#dot-inner"></use>
</svg>
[ɔi, ɔɪ], boy
</div>
</div>
</div>
<p>If you merge caught-cot, it is preferred to use the strong vowel /a~ɑ/ rather than the weak /ɔ/ vowel.</p>
</article>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-angle-1.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="58.297225"
inkscape:cy="49.565338"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="opacity:1;fill:none;stroke:#000000;stroke-width:2.88527;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M -14.362416,10.777636 H 39.668139 L 39.543646,61.571338"
id="path997" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-angle-2.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="58.297225"
inkscape:cy="49.565338"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="opacity:1;fill:none;stroke:#000000;stroke-width:3.48084;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -13.760888,11.312577 h 52.859073 l -0.121795,49.6924 M 31.546889,55.280608 31.181504,18.985668 H -9.1326744"
id="path997"
sodipodi:nodetypes="cccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-loop-1.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="58.297225"
inkscape:cy="49.565338"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.47853;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m -19.165384,42.476241 c 17.7109418,0 50.4325,2.834134 56.735052,-9.455841 C 42.07245,24.239975 36.966764,12.030219 28.939336,10.26259 17.607335,7.7672985 5.2196746,13.807934 5.2997309,27.316876 c 0.093399,15.76044 0.600371,39.62448 0.600371,39.62448"
id="path1628"
sodipodi:nodetypes="csssc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-loop-2.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="58.297225"
inkscape:cy="49.565338"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.47853;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -19.165384,42.476241 c 8.855471,0 29.0977067,0.05348 40.300402,-0.419365 C 32.260682,41.587286 33.104667,30.401111 32.017194,24.46492 30.664159,17.079107 21.593359,14.070003 15.666077,19.626568 c -5.452087,5.11109 -4.334131,14.808885 2.482025,16.41034 5.816455,1.366575 15.803522,0.03102 19.421566,-7.51929 C 41.108784,21.132016 35.766022,11.880126 28.939336,10.26259 17.648475,7.5872983 5.2196746,11.856729 5.2997309,27.316876 c 0.081612,15.760505 0.600371,39.62448 0.600371,39.62448"
id="path1628"
sodipodi:nodetypes="csssssssc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-mound-1.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="53.426642"
inkscape:cy="52.239384"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.38618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M -22.108869,56.106179 C -14.825778,11.242338 38.195123,10.756797 47.420373,55.911962"
id="path879"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-mound-2.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="53.426642"
inkscape:cy="52.239384"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.38618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -22.108869,56.106179 c 7.283091,-44.863841 60.303992,-45.349382 69.529242,-0.194217 m -6.312012,0.09711 C 31.124413,19.412004 -6.9167823,19.549482 -16.379503,56.203287"
id="path879"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)"
sodipodi:docname="consonant-ohm-1.svg">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.0809783"
inkscape:cx="90"
inkscape:cy="111.42857"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.3829;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -19.24459,13.148591 c 0,0 0,30.618621 0.380829,46.994251 C -21.529562,31.009456 -4.9257819,20.311558 10.193462,28.381734 25.312709,36.45191 21.618323,59.419268 1.4344043,59.609684 H 45.610527"
id="path10"
sodipodi:nodetypes="cczcc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-ohm-2.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.0815278"
inkscape:cx="46.698843"
inkscape:cy="49.07336"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.23935;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -19.84984,16.034543 c 0,0 -0.30307,30.531598 0.0616,46.212329 C -22.340919,34.349758 -6.1386503,22.89355 8.3390101,30.621269 22.816673,38.348988 19.279058,60.341727 -0.04835834,60.524063 H 46.264501 m -59.945353,0.01187 C -15.14294,49.472101 -7.6483722,31.611945 3.5872891,36.084496 14.82295,40.557047 7.8284906,55.594643 2.5844517,59.662271"
id="path10"
sodipodi:nodetypes="cczccczc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-wave-1.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="58.297225"
inkscape:cy="49.565338"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.47853;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -23.818258,53.583103 c 12.457696,-18.611498 19.2118685,-30.018545 19.2118685,-30.018545 0,0 -3.1519473,26.266227 11.2569551,26.116134 C 21.059467,49.5306 25.712341,24.014836 25.712341,24.014836 l 23.414466,29.868453"
id="path1666"
sodipodi:nodetypes="ccscc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="consonant-wave-2.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="21.317736"
inkscape:cy="37.150128"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.47853;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -23.818258,41.87587 c 12.457696,-18.611498 19.2118685,-30.018545 19.2118685,-30.018545 0,0 -3.1519473,26.266227 11.2569551,26.116134 C 21.059467,37.823367 25.712341,12.307603 25.712341,12.307603 c 0,0 -0.388919,33.246159 -0.450279,46.303607 -0.06136,13.057448 21.463261,3.752323 10.356399,-7.429586 C 24.5116,39.999714 15.91947,49.346229 15.806222,57.035236 15.607091,70.555334 3.4986177,69.042654 3.4986177,69.042654"
id="path1666"
sodipodi:nodetypes="ccscszsc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="example-diacritic-1.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.0815278"
inkscape:cx="60.769414"
inkscape:cy="44.743954"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.24027;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M -12.49308,54.124347 C -5.9981578,46.701579 19.981528,12.37128 34.827063,27.835378"
id="path2316" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="example-diacritic-2.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.0815278"
inkscape:cx="60.769414"
inkscape:cy="44.743954"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.24027;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 10.548428,74.227676 C 11.012351,40.979863 10.548428,0 10.548428,0"
id="path2336" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="example-diacritic-3.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.0815278"
inkscape:cx="60.769414"
inkscape:cy="44.743954"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.24027;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M -10.328105,40.51594 C 2.9710192,23.505431 5.4452756,24.8972 9.0020177,28.144661"
id="path1720" />
<path
style="fill:none;stroke:#000000;stroke-width:3.24027;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 11.800209,41.518467 C 25.099333,24.507959 27.57359,25.899728 31.130332,29.147189"
id="path1720-1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="example-diacritic-4.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.0815278"
inkscape:cx="60.769414"
inkscape:cy="44.743954"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.24027;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 1.1153276,75.619445 C 1.5792506,42.371632 -4.7610301,-0.77320496 -4.7610301,-0.77320496"
id="path2336"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:3.24027;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 18.486582,75.53969 C 18.950505,42.291878 24.36294,-0.54367674 24.36294,-0.54367674"
id="path2336-3"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="example-diacritic-4.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2">
<linearGradient
id="linearGradient837"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop835" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.0815278"
inkscape:cx="60.769414"
inkscape:cy="44.743954"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.24027;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 1.1153276,75.619445 C 1.5792506,42.371632 -4.7610301,-0.77320496 -4.7610301,-0.77320496"
id="path2336"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:3.24027;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 18.486582,75.53969 C 18.950505,42.291878 24.36294,-0.54367674 24.36294,-0.54367674"
id="path2336-3"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,419 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="144mm"
height="134mm"
viewBox="0 0 144 134"
version="1.1"
id="svg2086"
inkscape:version="0.91 r13725"
sodipodi:docname="splash-example.svg">
<defs
id="defs2080" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.6121292"
inkscape:cx="241.84032"
inkscape:cy="286.50685"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:snap-nodes="false"
inkscape:window-width="1280"
inkscape:window-height="737"
inkscape:window-x="345"
inkscape:window-y="1192"
inkscape:window-maximized="1" />
<metadata
id="metadata2083">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 74.28368,125.8388 c 7.716716,0.0567 17.66287,-3.00021 17.66287,-3.00021 -7.728575,0.75483 -11.705277,-8.68907 -3.656764,-14.88826 8.473622,-6.52662 16.741714,1.69622 12.537484,9.40311 0,0 9.9255,-6.00754 13.58226,-15.93304 3.65677,-9.925505 -0.0285,-21.700754 -0.0285,-21.700754 -0.32462,7.753077 -11.95486,10.128877 -15.382179,2.110944 -3.162835,-7.399206 6.704419,-14.29939 12.537479,-9.925503 l 0.2612,0 c -4.99629,-8.865621 -10.0867,-14.092485 -19.589811,-16.850795 0,0 -4.157798,6.275859 -11.993722,6.275859 -7.835924,0 -10.730457,-6.1417 -10.730457,-6.1417 -5.196623,0.705098 -10.144414,2.28111 -14.627055,5.485145 0,0 1.497542,9.091381 -2.513421,14.394343 -4.164917,5.506507 -13.419623,4.934269 -13.419623,4.934269 -3.050245,5.451395 -2.793767,14.406746 -0.119919,22.004672 -0.04436,-9.145054 10.084391,-12.145862 14.109392,-5.966401 5.261096,8.077201 -5.350043,14.106041 -10.593906,11.387461 0,0 6.713939,9.40311 13.76627,13.05987 9.162623,4.16922 10.481686,5.2943 18.198402,5.35099 z"
id="path2715"
sodipodi:nodetypes="zcscscscccsccsccsccz"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 104.25379,110.05889 c 0,0 -1.74407,-1.6099 -0.13416,-4.29309 1.60991,-2.68318 4.02477,-0.93911 4.02477,-0.93911"
id="path2717"
sodipodi:nodetypes="csc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 104.26867,99.77481 4.62171,3.21869 c -1.77173,-1.16335 -2.4896,-3.85978 -0.74278,-4.91057 1.49339,-0.89834 3.46014,0.38456 2.84731,2.39339 l 1.73314,-4.621707"
id="path2719"
sodipodi:nodetypes="ccscc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 109.13797,102.12692 c -2.80604,-2.97109 1.11416,-4.58044 0.9491,-1.7744"
id="path2721"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 113.71841,93.791352 -2.59971,-1.320488 c 3.31452,-1.445513 3.81588,-2.763155 -0.1238,-3.878932 l 3.38375,-2.310852"
id="path2723"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 104.30994,105.38688 c -5.199426,-3.83767 -1.40302,-13.163608 7.22141,-7.26268"
id="path2725"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 112.39792,96.84498 c -7.26268,-3.548812 -9.44973,-6.231052 1.27922,-6.726236"
id="path2727"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.58799899;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 100.28769,69.482485 4.54447,-3.326873 c -1.67541,1.298249 -4.45886,1.102112 -4.885825,-0.891187 -0.365023,-1.704114 1.486765,-3.148021 3.187945,-1.916386 l -3.80902,-3.139327"
id="path2719-8"
sodipodi:nodetypes="ccscc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 97.317544,59.673307 -1.851599,2.019928 c 1.394435,-4.21574 -0.195447,-5.065678 -2.637129,-1.066073 L 93.389907,56.5312"
id="path2744"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="path2746"
cx="80.14817"
cy="61.076035"
r="2.6371267" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.58799899;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="path2748"
cx="98.608055"
cy="80.994759"
r="2.9176719" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.58799899;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="path2750"
cx="88.227875"
cy="103.10174"
r="2.5249088" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="path2752"
cx="47.380463"
cy="71.343994"
r="2.6932359" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.58799899;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="path2754"
cx="46.370499"
cy="100.29628"
r="1.9638178" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 65.447587,59.448872 -0.561091,3.703198 c -0.996433,-3.694204 -4.616597,-3.41472 -2.973782,1.514946 -5.520249,-11.619934 -7.157265,2.206506 -1.178291,-5.218145"
id="path2756"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 43.677263,82.734139 0.448874,6.73309 -5.218144,-0.168328"
id="path2758"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 48.334316,110.9009 c 5.105927,-2.35659 6.508655,1.12218 5.274255,4.54483"
id="path2760"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 58.882825,113.81857 4.657053,3.59098 -3.254327,4.3204"
id="path2762"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 58.433952,115.2213 3.366545,2.35658 -2.581019,3.31043"
id="path2764"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 71.170713,117.40955 0.224435,6.34033 c 0.214455,-7.42074 6.792237,-3.44863 2.861564,-0.0561 l 8.472472,0"
id="path2766"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 80.179448,123.5586 c 0,-25.23337 9.467969,-47.082379 20.100902,-57.556609"
id="path2768"
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 77.05476,123.74457 c 0,-12.85473 1.633136,-47.861094 2.902739,-60.081026"
id="path2770"
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 53.096217,110.93916 C 63.411744,96.65612 69.669242,76.040436 62.210321,60.805193"
id="path2772"
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 61.637026,115.81796 c 8.093724,-11.26773 3.057959,-11.9623 -2.813958,-14.02541"
id="path2774"
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 45.003943,84.037836 c 1.595711,0.841375 1.450644,1.334594 0.261116,1.827812"
id="path2778"
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 44.945919,86.910111 c 2.059914,0.667297 1.682748,1.218542 0.203089,1.624721"
id="path2780"
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 95.747466,62.945476 C 94.55794,63.960925 94.209785,62.829424 94.209785,61.901012"
id="path2782"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 93.426438,62.046075 c -1.450644,0.986438 -1.798799,0.40618 -1.59571,-0.928413"
id="path2784"
inkscape:connector-curvature="0" />
<g
id="g4236">
<path
sodipodi:nodetypes="czcsc"
inkscape:connector-curvature="0"
id="path4170"
d="m 131.06368,53.921665 c -2.31222,-10.808892 -20.24743,-9.359552 -19.80528,4.327218 0.44214,13.686771 18.53779,14.538256 19.94958,2.592256 -2.51471,4.537386 -8.48889,2.390692 -8.89986,-3.028966 -0.40297,-5.314354 5.29553,-8.903362 8.75556,-3.890508 z"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4172"
d="m 113.57155,61.544409 c 4.17136,-3.029081 1.48669,-4.237335 -0.48177,-5.004378"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="csc"
inkscape:connector-curvature="0"
id="path4174"
d="m 115.87554,58.694466 c 4.57834,-0.554765 4.8188,-8.95848 9.51402,-11.293199 5.71338,-2.840999 11.94584,8.39029 3.81041,9.844054"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
transform="matrix(0.95320168,0.30233516,-0.30233516,0.95320168,0,0)"
r="1.8362098"
cy="16.227854"
cx="138.86736"
id="path4176"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
id="g4242">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path4178"
d="m 95.559975,11.750237 c -1.795423,7.043049 -8.084088,11.135527 -16.42193,11.405302 -1.165404,7.383091 -0.903824,12.334427 4.31,19.580574 6.794033,-4.973036 15.988717,-3.493632 19.234985,3.369177 15.8918,-7.527684 12.80734,-35.908642 -7.123055,-34.355053 z"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4180"
d="m 108.07608,22.006228 -3.49274,1.618461 c 1.55525,-3.126086 1.61797,-4.693085 -2.26842,-2.134864 l 1.82319,-3.609735"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4182"
d="m 84.935943,28.359039 -4.074517,0.791615 c 6.704125,-0.71369 2.394422,5.447287 0.716204,2.488649 l 1.407035,5.179741"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4184"
d="m 81.173278,29.943673 c 3.449449,-1.013743 3.14207,3.050499 0.261115,1.1979"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4186"
d="m 108.32363,39.172195 c -1.34359,-3.636247 -0.67436,-4.931921 2.72668,-6.443713"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
transform="matrix(0.99223803,0.12435307,-0.12435307,0.99223803,0,0)"
r="1.5641787"
cy="24.649677"
cx="99.840569"
id="path4188"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<circle
cx="93.97242"
cy="10.555672"
r="1.7001941"
transform="matrix(0.99223803,0.12435307,-0.12435307,0.99223803,0,0)"
id="path4190"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4194"
d="m 84.538815,30.434003 c 11.590683,-1.700219 18.346075,2.36778 23.178555,5.372304"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4196"
d="m 93.223943,23.464743 c 3.457159,4.134418 8.161947,1.609797 12.170207,-3.028346"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g4290">
<path
sodipodi:nodetypes="cczc"
inkscape:connector-curvature="0"
id="path4198"
d="M 45.985169,19.976306 C 52.449837,20.429917 55.999384,24.430033 54.8095,30.577555 66.060797,33.433297 73.400392,20.063039 67.211353,12.49178 61.022313,4.920521 47.019539,7.8371391 45.985169,19.976306 Z"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cssssc"
inkscape:connector-curvature="0"
id="path4200"
d="m 56.722561,12.412768 c -3.203647,0.418813 -4.689284,1.489348 -4.033584,2.209205 0.655701,0.719856 1.698541,0.348204 0.765782,-0.414835 -0.932759,-0.763039 -2.834737,0.943729 -1.605454,2.053466 1.229285,1.109741 3.160462,-0.141754 2.964287,-1.774902 -0.196178,-1.633147 -1.555235,-3.579276 -1.555235,-3.579276"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path4202"
d="m 62.667405,23.753501 1.084008,-4.41148 3.416823,0.775017"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4204"
d="m 54.708994,16.656242 c 0.391118,1.346356 -1.017887,1.711016 -1.432721,0.945235"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
transform="matrix(0.92121798,0.38904683,-0.38904683,0.92121798,0,0)"
r="1.0881243"
cy="-1.5028253"
cx="59.317307"
id="path4206"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 38.893428,61.945683 c -1.723794,-5.6658 -8.002016,-1.359804 -4.669635,4.420855 3.332381,5.780662 9.249057,-0.952108 11.493314,2.856326"
id="path4224"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc" />
<g
id="g4260">
<path
sodipodi:nodetypes="zccccz"
inkscape:connector-curvature="0"
id="path4208"
d="M 25.734219,32.960189 C 14.785753,37.147631 13.28511,46.318981 13.977591,55.825684 c 6.485034,0.362988 9.408356,3.273837 9.386119,10.271961 10.174479,3.796049 26.127005,0.07107 27.40469,-8.502714 C 44.139864,54.347265 42.357554,46.324247 48.231651,39.618675 44.214685,34.686679 36.682684,28.772747 25.734219,32.960189 Z"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4210"
d="m 21.337106,42.06407 -1.370927,5.272966 -4.209993,-1.20859"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4212"
d="m 26.03996,65.576137 c 2.72031,-4.080468 4.012459,-2.924336 5.032574,0.680077"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4214"
d="m 36.400043,62.558104 2.108239,3.196365 c -2.792496,-5.698811 5.150111,-4.556511 2.675204,-1.202286 l 4.622787,-3.604758"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4216"
d="m 44.98338,39.302928 c -2.9567,1.351363 -4.861041,-1.175832 -4.169852,-3.828751"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4218"
d="m 35.583949,36.511127 -4.012458,-0.06801 0.06801,-3.604412"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="2.1762486"
cy="61.993927"
cx="17.856125"
id="path4220"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<circle
r="1.9722252"
cy="48.616859"
cx="49.591805"
id="path4222"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4226"
d="m 29.009748,63.037295 c 0.04615,-9.563056 4.419808,-17.623423 4.465609,-26.29959"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 20.758604,44.987803 c 12.309406,5.168588 0.473441,-10.107361 10.189391,-19.425926 5.223964,-5.010302 18.455454,-9.695299 32.201411,-4.43639"
id="path4228"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 41.454472,39.18668 c -4.511762,5.416675 2.970486,8.001667 12.559581,0.452804 9.589096,-7.54886 17.463144,-13.399875 26.168139,-13.195851 8.704995,0.204024 18.499681,1.256191 27.745608,8.23207"
id="path4230"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssc" />
<ellipse
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path4232"
cx="72.745079"
cy="64.584801"
rx="63.523876"
ry="62.755436" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.588;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.292307,132.71927 c -3.586025,-4.86675 3.842171,-4.86675 12.038799,-2.3053 8.196628,2.56145 13.575664,1.79301 9.989643,-3.07374"
id="path4234"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="vowel-example.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.057243"
inkscape:cx="25.136479"
inkscape:cy="24.543915"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.48084;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -24.402715,49.335233 h 12.307604 c 0,0 7.3545431,-18.311313 24.915392,-18.311313 17.560849,0 23.714653,18.137348 23.714653,18.137348 l 12.907974,-0.142135"
id="base-mound"
sodipodi:nodetypes="cczcc" />
<path
style="fill:none;stroke:#000000;stroke-width:3.48083;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -24.143392,69.705313 h 21.9728073 c 0,0 -8.7107683,-5.308618 -8.6254273,-18.536589 0.08534,-13.227971 11.8971468,-19.957943 22.88067,-20.100079 10.983523,-0.142135 24.132914,6.070091 24.270193,19.58669 0.137279,13.516599 -8.372985,19.018148 -8.372985,19.018148 l 21.57823,-0.142137"
id="base-omega"
sodipodi:nodetypes="cczzzcc" />
<circle
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.49272;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="dot-outer"
cx="12.504731"
cy="50.600185"
r="7.9595795" />
<circle
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.49272;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="dot-inner"
cx="12.50473"
cy="14.924212"
r="7.9595795" />
<circle
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.49272;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="dot-on"
cx="12.646866"
cy="31.411909"
r="7.9595795" />
<path
style="fill:none;stroke:#000000;stroke-width:3.49271999;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 12.646867,6.8224964 C 12.504729,-8.9545266 12.504729,-1.2792181 12.504729,-1.2792181"
id="emphasis-inner" />
<path
style="fill:none;stroke:#000000;stroke-width:3.49271999;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 12.50473,58.701897 c 0,15.634887 0,15.634887 0,15.634887"
id="emphasis-outer" />
<path
sodipodi:type="star"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.49272;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="triangle-inner"
sodipodi:sides="3"
sodipodi:cx="12.602679"
sodipodi:cy="15.606641"
sodipodi:r1="8.7842556"
sodipodi:r2="8.4311752"
sodipodi:arg1="-1.565766"
sodipodi:arg2="-0.51856839"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="M 12.646867,6.822496 20.187878,20.036981 4.9732929,19.960446 Z"
inkscape:transform-center-x="0.11044414"
inkscape:transform-center-y="-0.12140727" />
<path
style="fill:none;stroke:#000000;stroke-width:3.48083;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -24.143392,69.705313 c 21.9728073,0 54.541558,-0.600371 58.120012,-22.089973 C 35.199043,37.798618 21.290897,32.244199 12.511064,32.347864 3.742928,32.451391 -10.546513,37.771707 -8.5599175,48.096898 -4.5769567,68.798058 27.981866,69.673483 49.560097,69.531345"
id="base-loop"
sodipodi:nodetypes="ccssc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 25.4 75.438"
version="1.1"
id="svg8"
sodipodi:docname="vowel-example.svg"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.2355135"
inkscape:cx="21.529103"
inkscape:cy="49.565338"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
units="px"
width="96px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:3.48083;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -23.818259,62.288482 20.8628889,-0.300185 c 0,0 -9.0329669,-4.848276 -9.3245099,-16.960479 -0.291543,-12.112203 9.3248279,-23.403261 24.934154,-23.264372 15.609325,0.138888 23.455707,11.597083 23.283135,22.964186 -0.172572,11.367103 -8.574047,17.110571 -8.574047,17.110571 l 22.363816,0"
id="omega"
sodipodi:nodetypes="cczzzcc" />
<path
style="fill:none;stroke:#000000;stroke-width:3.48084;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -24.268537,40.074758 h 12.307604 c 0,0 6.4539867,-18.311313 24.014836,-18.311313 17.560849,0 24.765302,18.16122 24.765302,18.16122 h 12.757881"
id="mound"
sodipodi:nodetypes="cczcc" />
<circle
style="fill:none;stroke:#000000;stroke-width:3.48084;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1464"
cx="12.729321"
cy="44.127262"
r="10.431444" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="110mm"
height="110mm"
viewBox="0 0 110 110"
version="1.1"
id="svg847"
inkscape:version="1.0.1 (c497b03c, 2020-09-10)"
sodipodi:docname="zagreus.svg">
<defs
id="defs841" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.2916039"
inkscape:cx="210.09791"
inkscape:cy="210.64934"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1252"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:snap-nodes="false" />
<metadata
id="metadata844">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:1.165;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 54.173205,103.71444 c 16.87833,-0.14304 35.164479,-14.485148 39.64415,-23.516129 -7.141827,5.620129 -14.780853,8.004928 -20.047291,0.294065 -5.266439,-7.710864 0.99143,-22.369379 11.682975,-23.229634 10.691545,-0.860255 11.771552,14.472129 11.771552,14.472129 0,0 10.379109,-31.639154 -1.92205,-48.803556 C 88.100612,12.634519 78.034035,5.9142569 66.669826,4.3422549 c 0,0 -6.465508,9.5006771 -16.764149,12.6474841 -10.298644,3.146809 -22.794529,-3.095427 -22.794529,-3.095427 0,0 -0.180145,-0.117346 -4.185172,3.029461 -4.005027,3.146806 -8.687804,7.404833 -12.803744,15.194178 9.3291,-4.452625 16.620006,2.804308 12.448955,11.139261 C 18.239143,51.913877 6.6496452,50.150568 6.8679894,37.894907 -1.102393,54.05418 4.9528439,75.620933 13.771628,85.00924 c 13.044996,13.887448 23.523247,18.84824 40.401577,18.7052 z"
id="path1540"
sodipodi:nodetypes="zczzcccscscscsz" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 17.243501,63.514742 4.291101,7.313056 -7.437908,4.415952"
id="path1542"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 42.66504,89.014268 15.161889,0.28607 0.286073,8.29613"
id="path1544" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 45.79302,91.606188 9.164283,-0.2071 0.207103,5.48821"
id="path1546" />
<g
id="g1603"
transform="translate(3.3042585,-7.6296978)">
<ellipse
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1548"
cx="48.254879"
cy="31.065374"
rx="4.4527035"
ry="4.4123087" />
</g>
<ellipse
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1548-4"
cx="15.294922"
cy="39.738281"
rx="4.4527035"
ry="4.4123087" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1548-4-0"
cx="73.845245"
cy="65.89772"
rx="4.4527035"
ry="4.4123087" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 89.713085,50.619368 8.391919,-2.077021 c 0,0 -5.722853,0.949527 -6.722611,-2.575458 -0.732885,-2.584032 0.417086,-4.138651 1.608658,-4.835123 1.467715,-0.857879 3.493238,0.188224 3.493238,0.188224 L 94.613125,31.181967"
id="path1595"
sodipodi:nodetypes="ccsscc" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 88.297883,23.330076 81.3556,25.328082 c 0,0 3.665362,-4.761921 0.938096,-7.269974 -2.805499,-2.579998 -7.185908,1.792125 -7.185908,1.792125 L 75.557293,13.6648"
id="path1597"
sodipodi:nodetypes="ccscc" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 97.469473,46.564637 c 0,0 -3.88925,0.591876 -4.175154,-2.014895 -0.275994,-2.516412 2.102883,-2.191483 3.863626,-0.982151"
id="path1599"
sodipodi:nodetypes="csc" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 19.565397,66.947242 C 29.567895,58.778791 60.358055,46.09781 53.203006,27.716201"
id="path1605"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 95.187851,34.059098 C 76.704726,36.931286 70.647741,28.381478 82.086667,17.883465"
id="path1607"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 52.508219,89.096038 c 0,0 -1.471686,-19.40193 8.194374,-31.928572 9.666061,-12.526642 23.060529,-18.181298 34.894956,-20.035638"
id="path1609"
sodipodi:nodetypes="czc" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 41.295644,105.50336 c 5.341827,1.67647 16.446476,1.62886 22.408102,-0.0507"
id="path1611"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -0,0 +1,415 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<style type="text/css">
svg circle, svg path { fill: none; }
svg .glyph { stroke: #600; fill: none; }
svg .vowel { stroke: #044; }
svg .radical { stroke: #004; }
svg circle.radical { fill: black; }
svg .enclosure { stroke: #000; }
</style>
<div class="row-fluid">
<div class="span6 offset3">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="400px" preserveAspectRatio="xMidYMax" id="arena" viewBox="-105 -105 210 210">
<defs>
<path id="mound" d="M-10,0 H-8 A9,8 0,0,1 8,0 H10" class="glyph"></path>
<g id="mound2">
<path d="M-10,0 H-8 A9,8 0 0,1 8,0 H10" class="glyph"></path>
<path d="M-6,0 A7,6 0,0,1 6,0" class="glyph"></path>
</g>
<path id="omega" d="M-10,0 H-4 A7,7 0,1,1 4,0 H10" class="glyph"></path>
<g id="omega2">
<path d="M-10,0 H-4 A7,7 0,1,1 4,0 H10" class="glyph"></path>
<path d="M-1,-1 A4.5,4.5 0,1,1 1,-1" class="glyph"></path>
</g>
<path id="angle" d="M-10,0 H-6 L0,-8 L6,0 H10" class="glyph"></path>
<g id="angle2">
<path d="M-10,0 H-6 L0,-8 L6,0 H10" class="glyph"></path>
<path d="M-4,0 L0,-5.3333 L4,0" class="glyph"></path>
</g>
<path id="chalice" d="M-10,0 H-6 A15,15 0,0,1 -10,-6 A15,10 0,0,0 10,-6 A15,15 0,0,1 6,0 H10" class="glyph"></path>
<g id="chalice2">
<path d="M-10,0 H-6 A15,15 0,0,1 -10,-6 A15,10 0,0,0 10,-6 A15,15 0,0,1 6,0 H10" class="glyph"></path>
<path d="M-8.5,-3 A 14,9 0,0,0 8.5,-3" class="glyph"></path>
</g>
<path id="loop" d="M-10,0 H-8 C6,0 6,-10 0,-10 C-6,-10 -6,0 6,0 H10" class="glyph"></path>
<g id="loop2">
<path d="M-10,0 H-8 C6,0 6,-10 0,-10 C-6,-10 -6,0 6,0 H10" class="glyph"></path>
<circle r="2" cx="0" cy="-6" class="glyph"></circle>
</g>
<path id="weird_up" d="M -4,-4 A 4,6 0,0,0 0,-8 A 4,6 0,0,0 4,-4" class="vowel"></path>
<path id="weird_down" d="M -4,-8 A 4,6 0,0,0 0,-4 A 4,6 0,0,0 4,-8" class="vowel"></path>
</svg>
</div>
</div>
<hr>
<input type="text" value="shoof~l ihs ~ bihg dohrk" />
<h3>Available Sounds:</h3>
<p id="sounds_list"></p>
<script type="text/javascript">
var svg_ns = "http://www.w3.org/2000/svg";
var xlink_ns = "http://www.w3.org/1999/xlink";
function cos_deg (angle) { return Math.cos(angle * Math.PI / 180); }
function sin_deg (angle) { return Math.sin(angle * Math.PI / 180); }
function norm (vector) {
var norm = Math.sqrt(vector.x*vector.x + vector.y*vector.y);
return {x: vector.x / norm, y: vector.y / norm};
}
function sub (a, b) { return {x: a.x-b.x, y: a.y-b.y}; }
function add (a, b) { return {x: a.x+b.x, y: a.y+b.y}; }
function mul (a, b) {
if ("x" in a) { return {x: a.x*b, y: a.y*b}; }
return a*b;
}
// Table is the table of what letters mean what things!
// Each entry in the table is a list of columnar cells.
// Each column corresponds to a particular radical configuration.
// Each cell is a list of strings which will be recognized in parsing.
var table = {};
// Main consonants!
table["mound"] = [ ["m"], ["n"], [], ["ng"]];
table["omega"] = [ ["p"], ["t"], [], ["k", "c", "q"]];
table["angle"] = [ ["f", "ph"], ["s"], ["sh"], []];
table["loop"] = [ ["th"], [], ["ch"], []];
table["chalice"] = [["w"], ["r"], ["y"], ["h"]];
// Alternate (mostly, voiced) consonants!
table["mound2"] = [ [], [], [], []];
table["omega2"] = [ ["b"], ["d"], [], ["g"]];
table["angle2"] = [ ["v"], ["z"], ["zh"], []];
table["loop2"] = [ ["dh"], [], ["j"], []];
table["chalice2"] = [[], ["l"], [], []];
// Vowels in the eat/ate/at and oh/ow/aw tracks!
table["above double"] = [["ee"], ["ay"], ["a"]];
table["above single"] = [["ih"], ["eh", "e"], ["ah"]];
table["below"] = [["oh", "o"], ["ow", "ou"], ["aw"]];
// Vowels on the line!
table["on the line double"] = [["uh"], ["oo"]];
table["on the line single"] = [["~"], ["u"]];
// The weird ones!
table["weird up"] = [["i"]];
table["weird down"] = [["oy", "oi"]];
// These lists simply list out which radicals should be shown.
// four_rad_options contains the radicals array that should be passed to make glyphs in rows with four columns.
// three_rad_options contains the radicals array that will be passed to make glyphs in rows with three columns.
// And so on.
// These are consumed in make_new_glyph().
// Basically, each glyph has a number of locations that radicals are placed.
// If a shape is in the third column of a four-column row, then we look at four_rad_options, and see that the
// third element is [null, "dot", "dot"]. So that shape will have no radical in the first position, and dots in
// both the second and third positions.
var four_rad_options = [];
four_rad_options.push(["dot", null, null]);
four_rad_options.push(["line", null, null]);
four_rad_options.push([null, "dot", "dot"]);
four_rad_options.push([null, "line", "line"]);
var three_rad_options = [];
three_rad_options.push(["line", null]);
three_rad_options.push([null, null]);
three_rad_options.push([null, "line"]);
var two_rad_options = [];
two_rad_options.push([null, null]);
two_rad_options.push(["line", "line"]);
var one_rad_options = [];
one_rad_options.push([]);
var rad_options = [one_rad_options, two_rad_options, three_rad_options, four_rad_options];
// Invert the table of shapes, and the lists of radical options, into a lookup table.
// The keys are phrases recognized as coding a sound, and the values are objects describing the appearance of the glyph that should result.
var lookup = {};
$.each(table, function(shape_name, row) {
var radical_options = rad_options[row.length - 1];
$.each(row, function(radical_index, valid_sequences) {
var current_radicals = radical_options[radical_index];
$.each(valid_sequences, function(i, phrase) {
lookup[phrase] = {name: shape_name, radicals: current_radicals};
});
});
});
var consonant_shapes = "mound omega angle chalice loop ";
consonant_shapes = consonant_shapes + consonant_shapes.replace(/ /gi, "2 ");
consonant_shapes = consonant_shapes.split(" ");
console.log(consonant_shapes);
var vowel_shapes = "above double,above single,below,on the line double,on the line single,weird up,weird down".split(",");
// Defines the positions of the radicals for glyphs. Should perhaps be moved to live in the XML?
// Maybe each symbol could be a group, and then have radical shapes identified by their classes,
// and then dynamically shown/hidden?
var consonant_radical_positions = {
"mound": [{x: 0, y: -8}, {x: -3, y: -7}, {x: 3, y: -7}],
"omega": [{x: 0, y: -16}, {x: -3, y: -15}, {x: 3, y: -15}],
"angle": [{x: 0, y: -12}, {x: -3, y: -10}, {x: 3, y: -10}],
"loop": [{x: 0, y: -14}, {x: -3, y: -14}, {x: 3, y: -14}],
"chalice": [{x: 0, y: -7}, {x: -3, y: -7}, {x: 3, y: -7}]
};
// Ensure that all the doubled glyphs have the same radical positions as their singled counterparts.
$.each(consonant_shapes, function(i, name) {
if (name.slice(0, -1) in consonant_radical_positions) {
consonant_radical_positions[name] = consonant_radical_positions[name.slice(0, -1)];
}
});
var radical_backup = 3.5;
var radical_length = 3;
var radical_length_vowel = 6;
var vowel_radius = 4;
var inset = 5;
var word_radius = [14, 14, 14, 16, 20, 24, 28, 32];
var sentence_radius = 80;
var the_big_word;
function new_glyph(symbol_name, radicals) {
var glyph;
if ($.inArray(symbol_name, consonant_shapes) != -1) {
glyph = new_consonant(symbol_name, radicals);
}
if ($.inArray(symbol_name, vowel_shapes) != -1) {
glyph = new_vowel(symbol_name, radicals);
}
glyph.name = symbol_name;
glyph.radicals = radicals;
return glyph;
}
function new_vowel(symbol_name, radicals) {
var s = {handles: {}, vectors: []};
s.handles.circle_in = {x: 0, y: 0};
s.handles.circle_out = {x: 0, y: 0};
s.handles.radicals = [];
if (symbol_name == "weird up" || symbol_name == "weird down") {
s.e = ns_elem('use', svg_ns, {'xlink:href': '#' + symbol_name.replace(" ", "_")}, xlink_ns);
s.handles.center = {x: 0, y: -6};
}
else {
s.e = ns_elem('g', svg_ns);
if (symbol_name.indexOf("above") != -1) { s.handles.center = {x: 0, y: -8}; }
if (symbol_name.indexOf("below") != -1) { s.handles.center = {x: 0, y: 8}; }
if (symbol_name.indexOf("on the line") != -1) { s.handles.center = {x: 0, y: 0}; }
s.handles.radicals.push({x: 0, y: s.handles.center.y - vowel_radius});
s.handles.radicals.push({x: 0, y: s.handles.center.y + vowel_radius});
var vowel_out = $(ns_elem('circle', svg_ns));
vowel_out.attr({r: vowel_radius, cx: s.handles.center.x, cy: s.handles.center.y}).attr('class', 'vowel');
$(s.e).append(vowel_out);
if (symbol_name.indexOf("double") != -1) {
var vowel_in = $(ns_elem('circle', svg_ns));
vowel_in.attr({r: vowel_radius/3, cx: s.handles.center.x, cy: s.handles.center.y}).attr('class', 'vowel');
$(s.e).append(vowel_in);
}
$.each(radicals, function (index, value) {
if (value != "line") { return true; }
var start = s.handles.radicals[index]; var d = norm(sub(start, s.handles.center));
var end = { x: start.x + d.x * radical_length_vowel, y: start.y + d.y * radical_length_vowel};
var line = ns_elem('path', svg_ns);
$(line).attr('d', 'M ' + start.x + ', ' + start.y + ' L ' + end.x + ', ' + end.y);
$(line).attr('class', 'radical')
$(s.e).append(line);
});
}
$.each(s.handles, function(index, value) { if (index != "radicals") { s.vectors.push(value); } });
$.each(s.handles.radicals, function(index, value) { s.vectors.push(value); });
return s;
}
function new_consonant(symbol_name, radicals) {
var s = {e: ns_elem('g', svg_ns), handles: {}, vectors: []};
s.handles.center = {x: 0, y: 0};
s.handles.circle_in = {x: -10, y: 0};
s.handles.circle_out = {x: 10, y: 0};
s.handles.radicals = {};
$.extend(true, s.handles.radicals, consonant_radical_positions[symbol_name]);
$(s.e).append(ns_elem('use', svg_ns, {'xlink:href': '#' + symbol_name}, xlink_ns));
$.each(radicals, function(index, value) {
var radical_position = s.handles.radicals[index];
if (value == "dot") {
var circle = $(ns_elem('circle', svg_ns));
circle.attr({r: 1, cx: radical_position.x, cy: radical_position.y});
circle.attr('class', 'radical');
$(s.e).append(circle);
}
if (value == "line") {
var d = norm(sub(radical_position, s.handles.center));
var start = sub(radical_position, mul(d, radical_backup));
var end = add(radical_position, mul(d, radical_length));
var line = $(ns_elem('path', svg_ns));
line.attr('d', 'M' + start.x + ',' + start.y + ' L' + end.x + ',' + end.y);
line.attr('class', 'radical');
$(s.e).append(line);
}
});
$.each(s.handles, function(index, value) { if (index != "radicals") { s.vectors.push(value); } });
$.each(s.handles.radicals, function(i, vector) { s.vectors.push(vector); });
return s;
}
function transform_for_circle(radius, angle, glyph) {
var group = ns_elem('g', svg_ns);
$(group).attr('transform', 'rotate(' + (-1*angle) + ') translate(0 ' + radius + ')');
$(group).append(glyph.e);
$.each(glyph.vectors, function (name, value) {
// Translate the vector.
var translated = {};
translated.x = value.x;
translated.y = value.y + radius;
// Rotate the vector.
var rotated = {};
rotated.x = translated.x * cos_deg(-angle) - translated.y * sin_deg(-angle);
rotated.y = translated.x * sin_deg(-angle) + translated.y * cos_deg(-angle);
// Change the vector in-place.
value.x = rotated.x;
value.y = rotated.y;
});
glyph.e = group;
return glyph;
}
function enclose(radius, list_of_glyphs) {
var length = list_of_glyphs.length;
angle_step = 360 / length;
var enclosure = {e: ns_elem('g', svg_ns), children: [], handles: {}, vectors: [] };
$.each(list_of_glyphs, function (index, glyph) {
var the_glyph = transform_for_circle(radius, angle_step*index, glyph);
enclosure.children.push(the_glyph);
$(enclosure.e).append(the_glyph.e);
$.each(the_glyph.vectors, function (i, vector) { enclosure.vectors.push(vector); });
});
$.each(enclosure.children, function (index) {
var current = enclosure.children[index];
var next = enclosure.children[(index+1) % length];
var npd = "M " + current.handles.circle_out.x + " " + current.handles.circle_out.y;
var arc = "A" + radius + "," + radius + " 0,0,0 ";
if (length != 1) {
npd = npd + arc + next.handles.circle_in.x + "," + next.handles.circle_in.y + " ";
}
else {
npd = npd + arc + "0," + (next.handles.circle_in.y - radius - radius) + " ";
npd = npd + arc + next.handles.circle_in.x + "," + next.handles.circle_in.y + " ";
}
var arc = $(ns_elem('path', svg_ns)).attr('d', npd).attr('class', 'enclosure');
$(enclosure.e).append(arc);
});
$(enclosure.e).attr('transform', 'translate(0 ' + (-radius - inset) + ')');
$.each(enclosure.vectors, function (i, vector) {
vector.y = vector.y - radius - inset;
});
enclosure.handles.circle_in = {x: 0, y: 0};
enclosure.handles.circle_out = {x: 0, y: 0};
enclosure.vectors.push(enclosure.handles.circle_in);
enclosure.vectors.push(enclosure.handles.circle_out);
return enclosure;
}
function letters_from_string(str) {
str = str.toLowerCase();
str = str.replace(/x/g, "ks");
str = str.replace(/qu/g, "kw");
str = str.replace(/q/g, "k");
var tokens = [];
while (str.length != 0) {
var results = consume_token(str);
str = results.str;
if (results.success) {
tokens.push(results.token);
}
}
return tokens;
}
function consume_token(str) {
if (str[0] == " ") {
return consume_token(str.slice(1));
}
var out = {success: false};
var the_tip = str.slice(0,2);
if (the_tip in lookup) {
out.success = true;
out.str = str.slice(2);
out.token = lookup[the_tip];
console.log(out);
console.log(the_tip, out.token);
}
else if (the_tip[0] in lookup) {
out.success = true;
out.str = str.slice(1);
out.token = lookup[the_tip[0]];
}
return out;
}
function interpret_input(str) {
var words = [];
$.each(str.split(" "), function (i, word) {
var tk = letters_from_string(word);
var glyphs = [];
$.each(tk, function (i, letter_glyph) {
glyphs.push(new_glyph(letter_glyph.name, letter_glyph.radicals));
});
var r = word_radius[word_radius.length-1];
if (glyphs.length < word_radius.length) {
r = word_radius[glyphs.length];
}
words.push(enclose(r, glyphs));
});
var out = enclose(sentence_radius, words);
out = transform_for_circle(sentence_radius+inset, 0, out);
return out;
}
$(document).ready(function () {
function change_word(str) {
if (the_big_word && the_big_word.e) { $(the_big_word.e).remove(); }
the_big_word = interpret_input(str);
$('svg').append(the_big_word.e);
}
change_word($('input').val());
$('input').change(function() { change_word($(this).val()); });
$('input').keyup(function() { change_word($(this).val()); });
$('svg').append(the_big_word.e);
$('#sounds_list').text(Object.keys(lookup).join(", "));
});
</script>
<script type="text/javascript">
function ns_elem () { if (typeof arguments[0] === "undefined") { console.log('There was an error in the element assist function. Called with no valid tag name!');} var elem; if (typeof arguments[1] === "string") { elem = document.createElementNS(arguments[1], arguments[0]); for (var i=2; i<arguments.length; i+=2) { if (typeof arguments[i+1] === "undefined") { for (var key in arguments[i]) { elem.setAttribute(key, arguments[i][key]); } break; } else { for (var key in arguments[i]) { elem.setAttributeNS(arguments[i+1], key, arguments[i][key]); } } } } else { elem = document.createElement(arguments[0]); for (var i=1; i<arguments.length; i+=2) { if (typeof arguments[i+1] === "undefined") { for (var key in arguments[i]) { elem.setAttribute(key, arguments[i][key]); } break; } else { for (var key in arguments[i]) { elem.setAttributeNS(arguments[i+1], key, arguments[i][key]); } } } } return elem;}
</script>
</article>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,175 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<script type="text/javascript">
var ns = "http://www.w3.org/2000/svg";
var margin = 5;
var max_aspect_ratio = 3;
$(document).ready(function() { clear(document.getElementById('svg')); });
var ns = "http://www.w3.org/2000/svg";
var margin = 5;
var max_aspect_ratio = 3;
clear(document.getElementById('svg'));
function clear(panel) {
while (panel.children.length > 0) {
panel.children[0].remove();
}
panel.appendChild(new_rect(50, 50, 400, 600));
}
function generate() {
var svg = document.getElementById('svg');
clear(svg);
while (svg.children.length < document.getElementById('complexity').value) {
mutate(choose(svg.children));
}
}
function mutate(panel) {
choose(split_types(panel))(panel);
}
function split_types(panel) {
var splits = [];
var aspect_ratio = get_aspect_ratio(panel);
if (aspect_ratio < 5) {
splits.push(split_vertical);
splits.push(split_vertical);
splits.push(split_vertical);
splits.push(multisplit_vertical);
}
if (aspect_ratio > 0.2) {
splits.push(split_horizontal);
splits.push(split_horizontal);
splits.push(split_horizontal);
splits.push(multisplit_horizontal);
}
return splits;
}
function multisplit_vertical(panel) {
var x = parseFloat(panel.getAttribute('x'));
var y = parseFloat(panel.getAttribute('y'));
var width = parseFloat(panel.getAttribute('width'));
var height = parseFloat(panel.getAttribute('height'));
var n = choose([2,2,3,3,3,4]);
var results = [];
var step = (height + (1-n)*2*margin) / n;
for (var i=0; i<n; i++) {
var child = new_rect(x, y + i*(step + 2*margin), width, step);
panel.parentElement.appendChild(child);
results.push(child);
}
panel.remove();
return results;
}
function split_vertical(panel) {
var x = parseFloat(panel.getAttribute('x'));
var y = parseFloat(panel.getAttribute('y'));
var width = parseFloat(panel.getAttribute('width'));
var height = parseFloat(panel.getAttribute('height'));
var distance = Math.random()*(height - 2*margin);
var child_one = new_rect(x, y, width, distance - margin);
var child_two = new_rect(x, y + distance + margin,
width, height - distance - margin);
panel.parentElement.appendChild(child_one);
panel.parentElement.appendChild(child_two);
panel.remove();
return [child_one, child_two];
}
function multisplit_horizontal(panel) {
var x = parseFloat(panel.getAttribute('x'));
var y = parseFloat(panel.getAttribute('y'));
var width = parseFloat(panel.getAttribute('width'));
var height = parseFloat(panel.getAttribute('height'));
var n = choose([2,2,3,3,3,4]);
var results = [];
var step = (width + (1-n)*2*margin) / n;
for (var i=0; i<n; i++) {
var child = new_rect(x + i*(step + 2*margin), y, step, height);
panel.parentElement.appendChild(child);
results.push(child);
}
panel.remove();
return results;
}
function split_horizontal(panel) {
var x = parseFloat(panel.getAttribute('x'));
var y = parseFloat(panel.getAttribute('y'));
var width = parseFloat(panel.getAttribute('width'));
var height = parseFloat(panel.getAttribute('height'));
var distance = Math.random()*(width - 2*margin) + margin;
var child_one = new_rect(x, y, distance - margin, height);
var child_two = new_rect(x + distance + margin, y,
width - distance - margin, height);
panel.parentElement.appendChild(child_one);
panel.parentElement.appendChild(child_two);
panel.remove();
return [child_one, child_two];
}
function new_rect(x, y, width, height) {
var rect = document.createElementNS(ns, 'rect');
rect.setAttribute("x", x);
rect.setAttribute("y", y);
rect.setAttribute("width", width);
rect.setAttribute("height", height);
return rect;
}
function get_aspect_ratio(p) {return parseFloat(p.getAttribute('width')) / parseFloat(p.getAttribute('height'));}
function choose(o) {return o[Math.floor(Math.random() * o.length)];}
</script>
<p>Here, have a thing that generates layouts for comic pages! It's very rough, and silly.<p>
<label for="complexity">Number of panels:</label><input type="number" id="complexity" value="5"/>
<button onclick="generate()">generate the thing!</button>
<svg id="svg" style="stroke-width:2px; stroke:black; fill:none; display:block;"
width="50%"
viewBox="0 0 500 700"
preserveAspectRatio="meet"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" />
</article>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h1>Distributed Speakers</h1>
<h4>(project idea)</h4>
<div class="important">
<p>So you want to have a rally. You want to hold a sports competition for your large neighborhood. Maybe you want to broadcast announcements across your company picnic. Maybe you want to throw an impromptu dance party!</p>
<p>The common line between all of these scenarios is this: you need a sound system, but you do not have one.</p>
</div>
<p>I first conceived of this while thinking about the specific issue of a dance party. Here's my solution:</p>
<ul>
<li>Everyone downloads this free app.</li>
<li>Everyone directs the app to the streaming media source of choice - a web radio station or something.</li>
<li>Each phone in the network listens to the audio it hears, and synchronizes what it's playing to the surroundings.</li>
</ul>
<p>I liked this idea because I have friends I like dancing with, but we rarely have a sound system set up and in place for dancing in public spaces. But anywhere you need to communicate something to a large number of people but you don't have a sound system, this app would be useful.</p>
<h3>Let's talk about issues.</h3>
<p class="important">How do you sync up a bunch of audio sources together on the fly, with limited communication between them?</p>
<p>That's the biggest problem, but there are smaller issues - well, they're really just the obstacles we'd need to face:</p>
<ul>
<li>How do we make this accept a wide variety of stream types - web radio, youtube, soundcloud, etc.?</li>
<li>How do you do ad hoc networking between mobile devices?</li>
<li>How do we tie this in with youtube playlists, which people will frequently want to play music from?</li>
<li>Is this even possible, when considering the speed-of-sound delay? How far apart can sources be, such that they're perceived as one sound rather than two?</li>
</ul>
</article>
</body>
</html>

View File

@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h1 class="title">Stop Writing Tests.</h1>
<p>Okay, don't do that. Tests are great. But here's my rebuttal to the worship of test-driven development:</p>
<blockquote>Test-driven development doesn't work when you're designing the overall structure of your code.</blockquote>
<p>I'm specifically thinking about this in the context of knowing - knowing! - that test-driven development is the Right Thing, but not knowing how my application is even going to be structured in the first place. I can write basic tests for collision detection structures - and I did, although not by that name, and although they were terrible and missed a massive problem - but game development. How do I write tests for a system when every few commits is radically changing the structure? Tests work well when there's an easily verified success condition which stays consistent. Writing tests can interrupt the feedback loop that is useful for exploring new concepts and approaches!</p>
<p>But on the other hand, there's still a success condition there in something like a collision detection system. I could have written a test that adds a number of objects to a world in whatever collision structure I've chosen, and then tests manually to see if they're intersecting, and then sees if the structure reports the collisions correctly. In the end I actually <strong>did</strong> do that - it wasn't tremendously automated, but I made a thing that tested collisions (and reported the speed, mostly).</p>
<p>The place it falls apart is something like the component system. The point of tests is to see whether something is operating properly. But how do you check if something is operating properly, when "properly" changes from one version to the next?</p>
<p>The broader form of this is something like the physics code. One object bouncing off another has some broadly-defined "right-feeling" characteristics, but something like that is incredibly hard - impossible? - to write a programmatic test for. For one thing, testing of live behaviors is a pain. For another, you'd have to check programmatically if it feels right. If that's possible, then please, please tell me.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,140 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/javascript" src="/static/flot/jquery.flot.js"></script>
<script type="text/javascript" src="/static/flot/jquery.flot.resize.js"></script>
<script type="text/javascript" src="engine.js"></script>
<script type="text/javascript" src="supporting_math.js"></script>
<h1>Dynamic Systems in Games</h1>
<p>Here, take a look at this simple game I coded up. Your task is to keep an engine's temperature as high as possible, to maximize power output, without dmaaging the engine. The threshhold for damage is at <span data-var="damage_threshhold">10</span> degrees, and the engine will stop burning and deignite if it goes under <span data-var="ignition_threshhold">2</span> degrees.</p>
<div class="row-fluid">
<div id="fuel-management-1" class="span4 offset4">
<script type="text/javascript">
function heyo(sq) {
if (sq.temperature.value < sq.ignition_threshhold.value) {
return -sq.temperature_decay.value*sq.temperature.value;
}
return sq.temperature.value*sq.fuel_flow_rate.value - sq.temperature_decay.value*sq.temperature.value;
}
var e1=engine($('#fuel-management-1'), heyo);
</script>
<div class="flow_rate">
<label for="fuel">Fuel Flow Rate</label>
<input type="range" name="fuel" min=0 max=2 step=0.01 value=.8>
</div>
<input type="button" name="ignition" value="Ignite!">
<span><span class="temp-out">0</span> degrees</span>
<span class="noignition label label-info">No ignition</span>
<span class="damage label label-warning">DAMAGE!</span>
<div class="potential_plot" style="width:100%;height:300px"></div>
</div>
<span class="sidenote span4">The dotted lines indicate what the curve will look like if you adjust the fuel flow rate up or down a little bit.</span>
</div>
<p>It's interesting! Or maybe it's not. I threw this together trying to think of what an interesting way for an engine to work might be. The fundamental relationship here is:</p>
\[\frac{\partial T}{\partial t} = c_R T F - c_T (T - T_0)\]
<p>That is, at any given moment, the temperature is changing (\(\frac{\partial T}{\partial t}\)) by increasing proportional to the temperature times the fuel flow rate (\(c_R T F\)) and decreasing proportional to the difference between the chamber temperature and the ambient temperature (\(c_T (T - T_0)\)). The deignition isn't described in this formula, but it's pretty simple - the \(c_R T F\) factor becomes zero when the temperature is less than <span data-var="ignition_threshhold">2</span> degrees.</p>
<p>As it turns out, (surprise of surprises!) that differential equation description is pretty useless for actually understanding how this feels. The key was to look at the rate of heating/cooling (\(\frac{\partial T}{\partial t}\)) as a function of the temperature. See, the player changes \(F\), and I want to understand how the temperature's going to change. So I made graphs. On the x-axis is temperature. The slope of the graph, at any point, is given by \(\frac{\partial T}{\partial t}\). <span class="sidenote span4">Note that this is not just going to undo the partial derivative. In this graph, we're integrating against \(T\), not \(t\). Roughly, this graph should be thought of as one where the temperature would slide down hills. Anyway.</span> What this means is that, to see how the system will behave, you find the current temperature (on the x-axis, remember), look at the corresponding point on the graph, and simply observe which direction it will fall.</p>
<p>As a slight digression, this was inspired by the concept of a <a href="http://en.wikipedia.org/wiki/Gravity_well">potential plot</a>, but it's different enough that I don't feel comfortable calling it one. The point is to get a sense for where the temperature is stable and whether it's going to be increasing or decreasing from various points. Since the temperature will always tend to fall down the graph, a stable point is horizontal.</p>
<p>Well. This is <em>terrible</em>, and describes why this doesn't feel fun. There are no stable points! That's not quite true - there are stable points under one condition: when the fuel flow rate is exactly 0.5. In those circumstances, the temperature always stays constant <em>everywhere</em>. This is no good! This means that all you have to do is raise the flow rate a little, to make the temperature roll out as far as you want it to go, and then drop it to 0.5 and it will stay exactly where you left it. BORING. Let's try some alternatives!</p>
\[\frac{\partial T}{\partial t} = c_R T F - c_T\]
<div class="row-fluid">
<div id="fuel-management-2" class="span4 offset4">
<script type="text/javascript">
function heyo2(sq) {
if (sq.temperature.value < sq.ignition_threshhold.value) {
return -sq.temperature_decay.value;
}
return sq.temperature.value*sq.fuel_flow_rate.value - sq.temperature_decay.value;
}
var e2=engine($('#fuel-management-2'), heyo2);
</script>
<div class="flow_rate">
<label for="fuel">Fuel Flow Rate</label>
<input type="range" name="fuel" min=0 max=0.25 step=0.001 value=0.1>
</div>
<input type="button" name="ignition" value="Ignite!">
<span><span class="temp-out">0</span> degrees</span>
<span class="noignition label label-info">No ignition</span>
<span class="damage label label-warning">DAMAGE!</span>
<div class="potential_plot" style="width:100%;height:300px"></div>
</div>
</div>
<p>This was actually the first formula I tried. Temperature always decreases by a constant rate, counterbalanced by the reaction rate (\(c_R T F\)) which is defined as before. It's got slightly better behavior - specifically, there is a stable point (the graph peaks somewhere for most flow rates), which moves around when you change the fuel flow rate.</p>
<p>On the other hand, it still has a lot of problems. </p>
<ul>
<li>For one thing, the stable point is extremely wide and flat - this means that you only have to get it roughly in the right position, and it'll generally stay happy by itself. This is supposed to be hard, dammit!</li>
<li>It drops very, very slowly. This is mostly just a matter of tweaking the constants, but it's just not very responsive. This was an advantage of the first model - where the temperature will fall exponentially in the absence of reactions. Unfortunately, the exponential behavior brought with it the cancellation of peaks. More on this later.</li>
<li>It just doesn't make sense! When you want to get it burning stably at a higher temperature, you... decrease the fuel flow? That don't make sense! I'm beginning to think that this is a fundamental problem of having the runaway reaction behavior - where over a certain stable temperature, it will just get hotter and hotter. That doesn't even make sense in the first place - why would it burn more and more with the same amount of fuel? This deserves more thought.</li>
</ul>
<p>Time for another try. There are two directions we can go here - try to describe entirely new behaviors from the ground up, or try to modify the existing system. I'll give both a try, once I have spare time!</p>
<div class="row-fluid">
<div id="fuel-management-3" class="span4 offset4">
<script type="text/javascript">
var gaussian = function (x) { return Math.exp(-x*x); };
var dgauss = function (x) { return x * gaussian(x); };
var recenter = function (fn, center, scale) { return function (x) { return fn( (x-center)/scale ); }; };
var e3=engine($('#fuel-management-3'), function (sq) {
if (sq.temperature.value < sq.ignition_threshhold.value) {
return 0;
}
return 5*recenter(dgauss,
10*sq.fuel_flow_rate.value + sq.ignition_threshhold.value,
Math.sqrt(10*sq.fuel_flow_rate.value)) (sq.temperature.value);
});
</script>
<div class="flow_rate">
<label for="fuel">Fuel Flow Rate</label>
<input type="range" name="fuel" min=0 max=1 step=0.01 value=0.1>
</div>
<input type="button" name="ignition" value="Ignite!">
<span><span class="temp-out">0</span> degrees</span>
<span class="noignition label label-info">No ignition</span>
<span class="damage label label-warning">DAMAGE!</span>
<div class="potential_plot" style="width:100%;height:300px"></div>
</div>
</div>
<p>Sweet! That's very much what I'm going for, at least in the peak. For the time being, I'm not worrying about what it's like far outside what we want to be the stable point. Now, though, it takes <em>too much</em> work to keep it stable.</p>
<div class="row-fluid">
<div id="fuel-management-4" class="span4 offset4">
<script type="text/javascript">
// describe a better potential graph
var f, t, i;
var e4=alt_engine($('#fuel-management-4'), function (sq) {
f = sq.fuel_flow_rate.value;
t = sq.temperature.value;
i = sq.ignition_threshhold.value;
return -5*(
exp_decay(0.5, 0.3) (t) +
exp_growth(0.5, 10) (t) +
Math.sqrt(1+60*f)*(make_bell(10*f+i, Math.sqrt(10*f+1)) (t)) +
-1*Math.sqrt(1+10*f)*(make_bell(10*f+i, Math.sqrt(f+1)) (t))
);
});
</script>
<div class="flow_rate">
<label for="fuel">Fuel Flow Rate</label>
<input type="range" name="fuel" min=0 max=1 step=0.001 value=0.1>
</div>
<input type="button" name="ignition" value="Ignite!">
<span><span class="temp-out">0</span> degrees</span>
<span class="noignition label label-info">No ignition</span>
<span class="damage label label-warning">DAMAGE!</span>
<div class="potential_plot" style="width:100%;height:300px"></div>
</div>
</div>
</article>
</body>
</html>

View File

@ -0,0 +1,179 @@
var cfg = {
xaxis: { min:0, max:15, tickSize: 3 },
yaxis: { min:-20, max:20, show: false },
series: { lines: { lineWidth: 4, }, points: { radius: 0.1, } },
colors: ['gray'],
};
function engine(element, reaction_rate_function) {
var e = {}; // the engine object!
e.container = $(element);
var q = {};
e.reaction_rate_function = reaction_rate_function;
e.quantities = q;
q.temperature = {
"update": function () {
var old_value = this.value;
this.value += reaction_rate_function(q)*timestep/1000
if (this.value < 0) { this.value = 0; }
if (old_value != this.value) { return true; }
},
"out": function () {
e.container.find('.temp-out').html(this.value.toFixed(2));
if (this.value > q.damage_threshhold.value) { e.container.find('.damage').show();}
else { e.container.find('.damage').hide(); }
if (this.value < q.ignition_threshhold.value) { e.container.find('.noignition').show(); }
else { e.container.find('.noignition').hide(); }
for (var i=0; i<this.graph_data.data.length; i++) {
this.graph_data.data[i][0] = this.value;
}
this.graph_data.data[1][1] = get_current_potential(this.value);
},
"value": 0,
"graph_data": {
data: [[0,cfg.yaxis.max+2],[0,0],[0,cfg.yaxis.min-2]],
lines: {show: true, lineWidth: 1},
points: {show: true, radius: 3},
color: 'red',
},
};
function get_current_potential(t) {
var data_series = all_graph_data[0].data;
var number_of_points = data_series.length;
var width = e.graph_config.xaxis.max - e.graph_config.xaxis.min;
var index = Math.floor((t / width) * number_of_points);
var below = data_series[index], above = data_series[index+1];
if (!below) { return 0; }
if (!above) { return null; }
//console.log("got current potential");
return below[1] + ((t - below[0])/(above[0] - below[0]))*(above[1] - below[1]);
}
q.fuel_flow_rate = {
"update": function () {
var old_value = this.value;
this.value = parseFloat(e.container.find('[name=fuel]').val());
if (old_value != this.value) {
return true;
}
},
"out": function () { generate_graph_points(); q.temperature.out(); },
"value": 0,
};
q.ignition_threshhold = {
"update": function () {},
"out": function() {
this.graph_data.data = [[this.value, cfg.yaxis.max+2], [this.value, cfg.yaxis.min-2], [-this.value, cfg.yaxis.min-2], [-this.value, cfg.yaxis.max+2], [this.value, cfg.yaxis.max+2]];
},
"value": 2,
"graph_data": {
data: [],
lines: {show: true, lineWidth: 0, fill: 0.2},
points: {show: false},
color: 'blue',
},
};
q.damage_threshhold = {
"update": function () {},
"out": function() {
this.graph_data.data = [[this.value, cfg.yaxis.min-2], [this.value, cfg.yaxis.max+2], [20*this.value, cfg.yaxis.max+2], [20*this.value, cfg.yaxis.min-2], [this.value, cfg.yaxis.min-2]];
},
"value": 10,
"graph_data": {
data: [],
lines: {show: true, lineWidth: 0, fill: 0.2},
points: {show: false},
color: 'yellow',
},
};
q.ignition_boost = {
"update": function () {},
"out": function() {},
"value": 5,
};
q.temperature_decay = {
"update": function () {},
"out": function () {},
"value": 0.5,
};
var ghost_count = 1, ghost_separation = 0.05;
var ghost_data_points = Array(1 + ghost_count*2);
var all_graph_data = [{"data": [], lines: {show: true}, points: {show: false}}];
for (var s=1; s<=ghost_count; s++) {
all_graph_data.push({"data": [], lines: {show: true, lineWidth: 0.5}, points: {show: true}});
all_graph_data.push({"data": [], lines: {show: true, lineWidth: 0.5}, points: {show: true}});
}
all_graph_data.push(q.temperature.graph_data);
all_graph_data.push(q.ignition_threshhold.graph_data);
all_graph_data.push(q.damage_threshhold.graph_data);
e.graph_config = {
xaxis: { min:0, max:15, tickSize: 3 },
yaxis: { min:-20, max:20, show: false },
series: { lines: { lineWidth: 4, }, points: { radius: 0.1, } },
colors: ['gray'],
};
function generate_graph_points() {
var step_size = 0.3;
var flow_value = q.fuel_flow_rate.value;
ghost_data_points[0] = $.extend({}, q, {"fuel_flow_rate": {"value": flow_value}, "temperature": {"value": 0}});
var potentials = [0];
for (var s=1; s <= ghost_count; s++) {
ghost_data_points[s] = $.extend({}, q);
$.extend(ghost_data_points[s], {
"fuel_flow_rate": {"value": flow_value + ghost_separation * s},
"temperature": {"value": 0}
});
potentials.push(0);
ghost_data_points[s+ghost_count] = $.extend({}, q);
$.extend(ghost_data_points[s+ghost_count], {
"fuel_flow_rate": {"value": flow_value - ghost_separation * s},
"temperature": {"value": 0}
});
potentials.push(0);
}
for (var s=0; s<potentials.length; s++) { all_graph_data[s].data = []; }
for (var i=e.graph_config.xaxis.min; i<e.graph_config.xaxis.max; i+=step_size) {
for (var s=0; s<potentials.length; s++) {
all_graph_data[s].data.push([i, potentials[s]]);
ghost_data_points[s].temperature.value = i;
potentials[s] += -1 * step_size * reaction_rate_function(ghost_data_points[s]);
}
}
//console.log("regenerated graph points");
}
function update_graph() {
e.graph_plot.setData(all_graph_data);
e.graph_plot.draw();
}
var a, b;
var timestep = 30;
e.container.ready(function () {
e.graph = e.container.find('.potential_plot');
e.graph_plot = $.plot(e.graph, [[]], e.graph_config);
jQuery.each(q, function(name, x) { x.out(); });
e.container.find('[name=ignition]').on('click', function() { q.temperature.value += q.ignition_boost.value; });
a = setInterval(jQuery.each, timestep, q, function(name, x) { if (x.update()) { x.out(); } });
b = setInterval(update_graph, timestep);
});
return e;
}
function temperature_derivative (fn, step) {
return function (quantities) {
var next_input = $.extend({}, quantities);
next_input.temperature = {"value": quantities.temperature.value + step};
return (fn(next_input) - fn(quantities))/step;
};
};
var step = 0.005;
function alt_engine(element, reaction_rate_potential_function) {
return engine(element, temperature_derivative(reaction_rate_potential_function, step));
}

View File

@ -0,0 +1,20 @@
function make_gaussian(mean, standard_deviation) {
return function (x) {
return Math.exp(-Math.pow((x-mean)/standard_deviation, 2))/(standard_deviation * Math.sqrt(2*Math.PI));
};
}
function make_bell(mean, standard_deviation) {
return function (x) {
return Math.exp(-Math.pow((x-mean)/standard_deviation, 2));
};
}
function exp_decay(height, width) {
return function (x) {
return height*Math.exp(-x/width);
};
}
function exp_growth(height, width) {
return function (x) {
return height*Math.exp(x/width);
};
}

View File

@ -0,0 +1,51 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<header>
<h1>Easy Peasy State Machinesy</h1>
</header>
<p>Why do I always have to actually use boolean flags (manually setting and unsetting them) to track the states of my entities?</p>
<pre>
<code>player.shielding = new Toggle()
keyboard.on('w pressed', player.shielding.next)</code></pre>
<p>I'm just throwing this syntax together off the top of my head. The syntax isn't important - what's important is that I should be able to describe this and have it happen.</p>
<pre>
<code>strobelight.color = new Sequence(['red', 'green', 'blue'])
strobelight.update = function(dt) {
strobelight.color.next()
strobelight.draw()
}</code></pre>
<p>Seriously, don't worry about this syntax - it's ugly and I just threw it together. The point is the kinds of things I should be able to do. This is all totally feasible.</p>
<pre>
<code>goomba.ai = new StateMachine(['sleep', 'suspicious', 'alert'])
goomba.ai['sleep'].transitions_to['suspicious'].on(goomba.near(player))
// the state 'sleep' should transition to 'suspicious' on the event goomba.near(player)
goomba.ai['sleep'].update = goomba.draw_snores
goomba.ai['suspicious'].transitions_to['alert'].on(goomba.sees(player))
goomba.ai['suspicious'].update = goomba.search_for_player
// shit we forgot to make a state for just patrolling
goomba.ai.add_state('patrol')
goomba.ai['patrol'].transitions_to['sleep'].after(5 seconds)
goomba.ai['patrol'].update = goomba.wander
// oh hey, she should occasionally wake up to patrol!
goomba.ai['sleep'].transitions_to['patrol'].after(5-10 seconds)</code></pre>
<p>So y'know. State machines are powerful. In order to really use them, you need a robust event system, or else they're a huge pain - it's still possible, you just... need to code it to check the conditions on each update loop. That could get expensive!</p>
<p>For the example of checking <code>goomba.near(player)</code> - which has an optional argument for the distance - that function checks if there's already a collision detection object attached to <code>goomba</code> for setting off proximity events. If there is, it returns the event handle, which can be used for binding behaviors to the firing of that event. If a proximity collider of the right size <em>doesn't</em> exist, then it creates one, and returns the event handle.</p>
<p></p>
</article>
</body>
</html>

View File

@ -0,0 +1,79 @@
<!doctype html>
<html>
<head>
<style type="text/css">
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: xx-large;
width: 100%;
height: 500px;
background: lightblue;
top: 0px;
bottom: 0px;
}
#time, #current-temp, #lows, #highs {
background: red;
position: absolute;
}
#time {
left: 40px;
top: 40px;
}
#current-temp {
right: 40px;
top: 40px;
background: red;
}
#lows {
bottom: 40px;
left: 40px;
}
#highs {
bottom: 40px;
right: 40px;
}
#top-alert {
background: blue;
}
</style>
</head>
<body>
<div class="container">
<div id="time">
10:04 AM
</div>
<div id="current-temp">
big 50F
</div>
<div id="lows">
low of 20F
</div>
<div id="highs">
high of 900F
</div>
<div id="top-alert">
Happy spring!
</div>
<div id="second-alert">
Today is a good day to start planting!
</div>
<div>
There's a frost coming up, probably a long one. It might a good time to do final harvest!
</div>
<div>
Monday is probably going to be the last frost. Get ready to plant!
</div>
<div>
Very high chance of rain today. You won't need to water.
</div>
<div>
Tonight it will go down to 20F. That's below freezing.
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,21 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h1>Games Diary?</h1>
<h2>January 15, 2015</h2>
<p>Starting a diary to try and record what I've been up to, and stuff, in a more freeform way.</p>
<p>Recently I've poked at Hatoful Boyfriend, I've been continuing to play New Super Mario Bros 2 from time to time, and I just finished the Forgotten Shores expansion for Monument Valley. Also I've been playing daily games of 3DS Smash Bros with my coworkers. Mostly though, I'm trying to beat Mass Effect.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,437 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<style type="text/css">
.accolades { display: inline-block; }
.thoughts { font-size: smaller; font-style: italic; }
#games-log p { margin: 0; }
.difficulty, .achievement {
border-radius: 2px;
border: 1px solid rgba(0,0,0,0.1);
}
.max {
background-color: black;
color: white;
}
.hard {
background-color: rgb(255,70,70);
}
.normal {
background-color: rgb(200,200,200);
}
.easy {
background-color: rgb(70,255,70);
}
.bad {
background-color: yellow;
}
</style>
<article id="games-log">
<h1>Games I've Played</h1>
<section>
<h2>Mostly Shooters and Stuff</h2>
<ul>
<li>Mass Effect 3 Multiplayer
<p class="thoughts">According to <a href="http://social.bioware.com">social.bioware.com</a> I've played 266 hours of this, as of December 9, 2015, over 840 games. I doubt that includes lobby time or character customization.</p>
</li>
<li>Halo: Combat Evolved
<div class="accolades">
<span class="difficulty max">Legendary solo</span>
</div>
<p class="thoughts">I really liked Halo: CE. It was a strong game, and I've had a soft spot for the Halo story - I don't think it's particularly deep or told with finesse, but it's <em>fun</em>!</p>
</li>
<li>Halo 2
<div class="accolades">
<span class="difficulty hard">Heroic co-op</span>
</div>
<p class="thoughts">I've been telling myself I'm going to beat Halo 2 on legendary by myself since the day I beat Halo: CE on legendary. I feel the overwhelming urge to be able to say I've beaten the whole trilogy on the hardest difficulty! On the other hand, it's really goddamn hard.</p>
</li>
<li>Halo 3
<div class="accolades">
<span class="difficulty max">Legendary co-op</span>
<span class="difficulty max">Legendary solo</span>
<span class="achievement max">All Skulls found</span>
</div>
</li>
<li>Halo 3: ODST
<div class="accolades">
<span class="difficulty hard">Heroic co-op</span>
</div>
</li>
<li>Halo: Reach
<div class="accolades">
<span class="difficulty hard">Heroic co-op</span>
</div>
</li>
<li>Gears of War
<div class="accolades">
<span class="difficulty max">Insane co-op</span>
</div>
</li>
<li>Gears of War 2
<div class="accolades">
<span class="difficulty hard">Hardcore co-op</span>
</div>
</li>
<li>Gears of War 3
<div class="accolades">
<span class="difficulty hard">Hardcore co-op</span>
</div>
</li>
<li>Half-Life</li>
<li>Half-Life: Blue Shift</li>
<li>Half-Life: Opposing Force</li>
<li>Half-Life 2 (and Episode 1) (and Episode 2)</li>
<li>Portal</li>
<li>Portal 2 (and co-op)</li>
<li>Army of Two</li>
<li>Army of Two: 40th Day</li>
<li>Operation Flashpoint: Dragon Rising
<div class="accolades">
<span class="achievement bad">Gave up after like 30 minutes</span>
</div>
<p class="thoughts">This game was terrible.</p>
</li>
<li>Resistance: Fall of Man
<div class="accolades">
<span class="difficulty hard">Hard? co-op</span>
</div>
</li>
<li>Resistance 3
<div class="accolades">
<span class="difficulty hard">Hard co-op</span>
</div>
<p class="thoughts">These games weren't very deep, but they had decent shooting and good weapons. Resistance 2 didn't have co-op, so I didn't play it - I just watched my friend do it.</p>
</li>
<li>Killzone 3
<div class="accolades">
<span class="difficulty hard">Veteran? co-op</span>
</div>
<p class="thoughts">This game offered alternate color modes for colorblindness, which is awesome. Shame the dialogue and plot was so literally laughable.
</p>
</li>
<li>Resident Evil 5
<div class="accolades">
<span class="difficulty hard">Veteran? co-op</span>
<span class="achievement max">Broke one level</span>
</div>
<p class="thoughts">My pal and I played this over one break, and we couldn't take it seriously. Once we beat it, we spent our hard-earned points on getting infinite ammo for a few weapons - and decided we'd try to unlock infinite ammo for every weapon. We didn't end up doing that, but we did get so practiced at going through one level - which drops the most money for the time you spend in it by <em>far</em> - that we beat the max difficulty time trial by <em>half</em>. The magnum in that game is ridiculously overpowered.</p>
</li>
<li>F.3.A.R.
<div class="accolades">
<span class="difficulty hard">Fearless? co-op</span>
</div>
<p class="thoughts">As far as I can tell, these people have no idea what the phrase "survival horror" even means.</p>
</li>
<li>Team Fortress</li>
<li>Team Fortress 2</li>
</ul>
</section>
<section>
<h2>Not Shooters</h2>
<ul>
<li>Pokemon: Red
<div class="accolades">
<span class="achievement normal">Beat the Elite Four</span>
</div>
</li>
<li>Pokemon: Silver</li>
<li>Pokemon: Diamond
<div class="accolades">
<span class="achievement normal">Beat the Elite Four</span>
</div>
</li>
<li>Crimson Skies</li>
<li>Prince of Persia (1989)</li>
<li>Prince of Persia: Sands of Time</li>
<li>Prince of Persia (2008)</li>
<li>Super Smash Brothers: Melee
<div class="accolades">
<span class="achievement hard">Every SP challenge</span>
</div>
</li>
<li>Super Smash Brothers: Brawl
<div class="accolades">
<span class="achievement max">Every achievement</span>
<span class="achievement bad">except one sticker</span>
</div>
</li>
<li>Mario Kart: Double Dash
<div class="accolades">
<span class="achievement max">Time Trial Staff Ghosts</span>
<span class="achievement max">Every Cup on Mirror</span>
</div>
</li>
<li>Mario Kart: Wii
<div class="accolades">
<span class="achievement max">Gold Medals on Everything</span>
</div>
</li>
<li>Okami
<div class="accolades">
<span class="achievement normal">All brush techniques</span>
</div>
</li>
<li>Legend of Zelda: Link's Awakening
<p class="thoughts">I think this might be my #1 pick for best Zelda game.</p>
</li>
<li>Legend of Zelda: Ocarina of Time</li>
<li>Legend of Zelda: Majora's Mask</li>
<li>Legend of Zelda: The Wind Waker</li>
<li>Legend of Zelda: The Minish Cap</li>
<li>Legend of Zelda: Twilight Princess</li>
<li>Legend of Zelda: Skyward Sword
<div class="accolades">
<span class="achievement bad">Unfinished</span>
</div>
</li>
<li>Super Mario 64</li>
<li>Super Mario Sunshine</li>
<li>Super Mario Galaxy
<div class="accolades">
<span class="achievement normal">120 Stars</span>
</div>
</li>
<li>New Super Mario Bros 2</li>
<li>Super Mario 3D World</li>
<li>Final Fantasy</li>
<li>Final Fantasy 6
<div class="accolades">
<span class="achievement bad">Halfway</span>
</div>
</li>
<li>Final Fantasy 7
<div class="accolades">
<span class="achievement bad">Halfway</span>
</div>
</li>
<li>Final Fantasy 8
<div class="accolades">
<span class="achievement bad">Halfway</span>
</div>
<p class="thoughts">I feel like including Final Fantasy games that I haven't finished is still reasonable because not finishing a Final Fantasy game involves a similar time commitment as finishing many other games.</p>
</li>
<li>No More Heroes
<div class="accolades">
<span class="difficulty normal">Mild</span>
<span class="achievement normal">All Beam Katanas</span>
</div>
</li>
<li>No More Heroes 2: Desperate Struggle
<div class="accolades">
<span class="difficulty normal">Mild</span>
</div>
</li>
<li>Guitar Hero
<div class="accolades">
<span class="difficulty max">Expert</span>
</div>
</li>
<li>Rock Band
<div class="accolades">
<span class="difficulty max">Expert Guitar</span>
<span class="difficulty normal">Medium Drums</span>
</div>
</li>
<li>Super Metroid</li>
<li>Metroid: Fusion</li>
<li>Metroid: Zero Mission</li>
<li>Metroid Prime</li>
<li>Metroid Prime 3: Corruption</li>
<li>Silent Hill 2
<div class="accolades">
<span class="achievement bad">Gave up</span>
<span class="achievement normal">In Water ending</span>
</div>
<p class="thoughts">As of September, 2013, this game is too scary for me. As of November, 2013, Glen and I finished it!</p>
</li>
<li>Psychonauts
<div class="accolades">
<span class="achievement bad">Unfinished</span>
</div>
<p class="thoughts">I know Psychonauts is everyone's cult fave, but it just didn't grab me.</p>
</li>
<li>Naruto: Ultimate Ninja Storm
<div class="accolades">
<span class="achievement max">100% Collection</span>
<span class="achievement max">100% Mission Completion</span>
</div>
<p class="thoughts">I wouldn't recommend it.</p>
</li>
<li>SSX: Deadly Descents
<div class="accolades">
<span class="achievement max">Platinum Completion Trophy</span>
</div>
<p class="thoughts">Well, this took me literally over two years to finally pick up the last few trophies, BUT!</p>
</li>
</ul>
</section>
<section>
<h2>More Indie</h2>
<ul>
<li>Jumper
<div class="accolades">
<span class="achievement bad">Unfinished</span>
</div>
</li>
<li>Jumper Two
<div class="accolades">
<span class="achievement bad">Unfinished</span>
</div>
</li>
<li>Jumper Three</li>
<li>Cave Story</li>
<li>Gish</li>
<li>World of Goo</li>
<li>Osmos</li>
<li>Jamestown</li>
<li>Kerbal Space Program
<div class="accolades">
<span class="achievement normal">Low Kerbin Orbit</span>
<span class="achievement normal">Round-trip Mun Mission</span>
<span class="achievement hard">Minmus Mission</span>
</div>
</li>
<li><a href="http://boson-x.com">Boson X</a>
<div class="accolades">
<span class="achievement normal">Discovered all the particles</span>
</div>
</li>
<li>Guns of Icarus
<div class="accolades">
<span class="achievement bad">unfinished</span>
</div>
</li>
<li>Hammerwatch
<div class="accolades">
<span class="achievement bad">unfinished</span>
</div>
<p class="thoughts">Ehhh... Just not very enticing, you know? And kinda too hard.</p>
</li>
<li>Monaco</li>
<li>AaaaaAAaaaAAAaaAAAAaAAAAA!!! A Reckless Disregard for Gravity</li>
<li>AaaaaAAaaaAAAaaAAAAaAAAAA!!! for the Awesome</li>
<li>Monument Valley
<div class="accolades">
<span class="achievement normal">main campaign</span>
<span class="achievement normal">Forgotten Shores</span>
</div>
</li>
</ul>
</section>
<section>
<h2>Extremely Indie</h2>
<p>(This section is kinda by nature going to be extremely incomplete; a lot of these games take like ten minutes to play, and often get forgotten. Talk to me and I'll recommend more, and just check out every game by <a href="http://aliendovecote.com/">porpentine</a>.)</p>
<ul>
<li><a href="aliendovecote.com/uploads/twine/empress/empress.html">With Those We Love Alive</a></li>
<li><a href="http://philome.la/baphomeme/queer-trans-mentally-ill-power-fantasy/play">Queer Trans Mentally Ill Power Fantasy</a></li>
</ul>
<section>
<h2>Extremely Memorable</h2>
<ul>
<li>Shadow of the Colossus
<div class="accolades">
<span class="difficulty max">Hard</span>
<span class="achievement hard">Time Trials (Normal)</span>
<span class="achievement max">Climbed the temple</span>
</div>
<p class="thoughts">I love this game so much I'm not even going to write about it. I'd say too much.</p>
</li>
<li>Ico
<p class="thoughts">Ico was very pretty, and had some of the same qualities that Shadow of the Colossus had, but it didn't tie together nearly so well.</p>
</li>
<li>Mirror's Edge
<div class="accolades">
<span class="difficulty normal">Easy</span>
<span class="achievement hard">Test of Faith (Pacifist)</span>
</div>
<p class="thoughts">This game has the best platforming I've had the pleasure of experiencing - and I'm a sucker for good platforming.</p>
</li>
<li>El Shaddai
<div class="accolades">
<span class="difficulty normal">Normal</span>
</div>
<p class="thoughts">I loved this game from an aesthetic point of view, but I kept waiting for the story to get interesting. I'm still waiting.</p>
</li>
<li>Zeno Clash
<p class="thoughts">The aesthetic really makes this game stand out.</p>
</li>
<li>Iji
<div class="accolades">
<span class="difficulty normal">Normal</span>
<span class="achievement normal">Somewhat Pacifist</span>
</div>
<p class="thoughts">Iji remains one of my favorite games of all time, for its excellent pseudo-morality system - one of the best I've seen.</p>
</li>
<li>Valkyrie Chronicles
<div class="accolades">
<span class="difficulty normal">Normal</span>
<span class="achievement normal">No deaths</span>
</div>
<p class="thoughts">Valkyrie Chronicles had such a cool gameplay and storytelling style that I have to include it.</p>
</li>
<li>No More Heroes
<div class="accolades">
<span class="difficulty normal">Mild</span>
<span class="achievement normal">All Beam Katanas</span>
</div>
<p class="thoughts">It's hard to describe this game. It was fascinating.</p>
</li>
<li>Elite Beat Agents
<div class="accolades">
<span class="difficulty hard">Sweatin'!</span>
<span class="difficulty max">Hard ROCK!</span>
</div>
<p class="thoughts">Probably my favorite rhythm game.</p>
</li>
<li>Silent Hill 2
<div class="accolades">
<span class="achievement bad">Gave up</span>
<span class="achievement normal">In Water ending</span>
</div>
<p class="thoughts">As of September, 2013, this game is too scary for me. As of November, 2013, Glen and I finished it!</p>
</li>
<li>OFF
<div class="accolades">
<span class="achievement normal">Batter ending</span>
<span class="achievement normal">Judge ending</span>
</div>
</li>
<li>Journey
<div class="accolades">
<span class="achievement hard">White Cloak</span>
</div>
<p class="thoughts">The only game I've played or seen with such an emotionally powerful and compelling anonymous multiplayer experience.</p>
</li>
<li>Bastion
<div class="accolades">
<span class="achievement normal">Both endings</span>
<span class="achievement normal">Altruist (all vigils)</span>
<span class="achievement normal">Mind Voyager (each trip to Who Knows Where)</span>
</div>
<p class="thoughts">Oh gosh this game. I loved it so much. Such a masterpiece of storytelling - two days after getting it I finished it, and two days later I finished my second playthrough. When I design games, this is what I try for - with maybe more female characters.</p>
</li>
<li><a href="aliendovecote.com/uploads/twine/empress/empress.html">With Those We Love Alive</a></li>
</ul>
</section>
<section>
<h2>Forgotten and Probably Indie</h2>
<ul>
<li>Lugaru HD</li>
<li>Beat Hazard</li>
</ul>
</section>
</article>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<span class="sidenote">This article is about identities, how we define them, and my thoughts on this as someone with an education in mathematics. It's also a trial run of a type of document I don't think I've seen before. <label for="passage-description">You can click on passages marked like this.</label><span class="collapsed" id="passage-description">When you do so, additional information relevant to that subject will be shown. In theory, this allows for presenting an overview of the topic while allowing seamless exploration of the subtopics within it. Let's see how it works.</span>
<h1>Identities and Spaces</h1>
<p>Gender is a complicated subject.</p>
<p>The simplest possible view of gender is that there are two kinds of people, men and women. They can be identified by their external characteristics: girls have long hair and wear pink, boys have short hair and wear blue. If you want to make it sound more scientific, you can point out that women are, by and large, more emotionally connected and empathic, and boys are stronger and physically larger.</p>
<p>Sooner or later, you have to confront the existence of people who do not fit into those categories. These people will often be incorrectly identified as one gender when they are not. One solution to this is to ask each person what gender they are, and trust them to know which one they actually fit.
</article>
</body>
</html>

View File

@ -0,0 +1,15 @@
{% extends "articles/article.template.html" %}
{% block title -%}
Features for a game-focused programming language or library
{%- endblock %}
{% block body -%}
<p>I like making games but sometimes I spend a lot of time doing stuff that is just annoying. What if a language or environment were designed to make those things easy?</p>
<hr>
{%- include "articles/snorlax_evaluation.article.html" -%}
<hr>
{%- include "articles/time_integration.article.html" -%}
<hr>
{%- include "articles/easy_peasy_state_machinesy.article.html" -%}
{%- endblock %}

View File

@ -0,0 +1,16 @@
{% extends "articles/article.template.html" %}
{% block title -%}
Features for a game-focused programming language or library
{%- endblock %}
{% block body -%}
<style type="text/css"> body { transform: scale(0.5); transform-origin: 0% 0%; } </style>
<p>I like making games but sometimes I spend a lot of time doing stuff that is just annoying. What if a language or environment were designed to make those things easy?</p>
<hr>
{%- include "articles/snorlax_evaluation.article.html" -%}
<hr>
{%- include "articles/time_integration.article.html" -%}
<hr>
{%- include "articles/easy_peasy_state_machinesy.article.html" -%}
{%- endblock %}

View File

@ -0,0 +1,45 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h1 class="title">Mindjail Engine!</h1>
<p>This is doubtlessly my most ambitious project. It's a little 2D game engine in python that I've been working on for a while now. It's got some basic physics (not accurate, but they feel right, and that's what counts) and it can zip along pretty respectably! I first started this project in my second year of college, but I soon abandoned it because (surprise of surprises!) I had a lot of classes to work on. When I finally picked it back up towards the start of 2013, I had grown a lot as a programmer and a human being, and there were changes to make. I still love this project, but the primary goal I had here was to never set a deadline for myself. The idea was that I should never say when this project will be done - because it's always in progress. I'm never fully committing myself to a flawed approach, and, although there's a bunch of ugly code here, I'm never afraid to go back and change how I did something.</p>
<h3>Tell me all about collision detection, it is my favorite subject!</h3>
<p>I was good at my data structures and algorithms class, and they're concepts I'm deeply familiar with. It's interesting, though, that nothing ever gave me such a good sense for how algorithmic complexities compare until I was doing real-time collision detection. It doesn't help that my chosen language was python - a decision that helped many things in this project, but not speed. <span class="sidenote">People will happily claim that python is too slow to ever make a real game with, but it's just not true. Computers are fast. I'm convinced that python is perfectly capable.</span> There've been a lot of data structures I used for this, and the source is somewhere - <span class="todo">I'm going to put those online at some point.</span></p>
<ul>
<li>When I started out, of course, I did the brute force N^2 test, because I just wanted to get objects moving around on the screen. This rapidly bottomed out once I got more than probably 30 objects on the screen. I don't remember, it was a long time ago! I knew from the start that I was going to have to do something better, so I did.</li>
<li>The next step was to roll something more advanced of my own. It had problems, but I'm still proud of how well it worked - I called it a sorted search list. The idea was that the list of objects in the world would be simultaneously kept sorted by each object's maximum and minimum extents on the x- or y-axes. When you're searching for a collision, then, you can cut down how many checks you do by searching upwards and downwards through the list until you find an object which is too far away to intersect - at which point no further objects could possibly be touching you. <span class="todo">I should make a picture.</span> Anyway, this system worked alright - if I remember correctly, I got up to around 50 objects on screen before it started slowing down a lot. I kept this around as a supporting data structure, but I didn't realize the massive flaws in it - in order to be certain about objects Foo and Bar not colliding, you need to run the check for Foo against Bar as well as Bar against Foo. Anyway.</li>
<li>
<p>After that I finally decided to ask the great oracle Google for help. Why didn't I use the knowledge of others before that? Well, they never really covered collision detection-specific data structures in my classes... I found out about a thing called a quadtree, and I was hooked. The idea is to recursively divide space into quadrants - objects northeast of center, northwest of center, and so on. This is the structure I spent the most time on, because I was dead-set on making it work. It was just so <em>cool!</em></p>
<p>Well. That was silly of me. There are a lot of problems. Suppose Foo is in the northwest corner, but Bar overlaps between northwest and northeast! There's no simple solution, and mine was to have a secondary data structure, which stored objects which couldn't fit into any of the corners.</p>
<p>At this point, I realized a structure-agnostic speedup I could use: Be mindful of whether objects move or not. If two objects never move, you never need to see if they collide. With this, the system got up to probably 80 objects on screen, with most of them stationary. Point being, it was a lot faster. I needed more, though! There was a huge problem:</p>
<p>The quadtree wasn't balancing. Balancing a regular tree data structure is one thing, but quadtrees have two dimensions to balance at once, which makes it infinitely more difficult... And I couldn't find a tutorial on it, nor figure it out on my own. All the tutorials either dealt with worlds that had a size limit for objects, or discrete-position finite-world grid systems. I didn't want to do either of those! So I started panicking.</p>
</li>
<li>The next solution was to try variations on quadtrees. I tried a bunch - changing what supporting data structure I used, switching from a strict quadtree - at each level dividing in quadrants - to a <a href="http://wikipedia.org/wiki/K-d_tree">k-D tree</a>: At each level, it divides the world in half, and switches the lines along which it divides the world each level. They're a little more flexible in a few ways, and in theory easier to balance (because you don't need to balance two dimensions at the same time so much). Unfortunately, there's a dearth of information about using them for collision detection, because everyone uses them for graphics and not much else. <span class="sidenote">At this point, I was running out of steam for recursive data structures, but I briefly considered extending my k-D tree to be the most general kind of <a href="https://en.wikipedia.org/wiki/Binary_space_partitioning">binary space partitioning tree</a>, wherein you don't just cycle your dividing axis through a set of options, but rather you divide along a completely new axis - not necessarily perpendicular! - at each level. This is used for raytracing a lot, but implementing it for these purposes would have been a pain. It would have just meant more balancing and trying to make the trees smart, which I didn't want to get too heavily into.</span> I gave up on this soon after, partly because...</li>
<li>I realized that my secondary data structure didn't work right. You needed to double-check every collision in order to be sure you had gotten all of them, and besides, sorting along one axis had little effect when you were frequently checking collisions which are grouped along the axis it doesn't sort on. I was basically back to square one. That's when I finally gave up on the tree - I would have needed to make it too smart in filling itself, I would have needed to balance it, I would have needed to have a good secondary data structure, and... It wasn't worth it. But then I found the light.</li>
<li>
<p>Spatial Hashing was something I'd heard of before, but mostly in the context of grid-based systems, finite worlds, and size-limited systems. I didn't want <em>any</em> of that. The typical spatial hashing scheme goes like this: You have a 2D array of lists. Each list corresponds to one grid space in your world, and so you only need to check against objects in adjacent squares. Here was my problem: Static arrays means you need to have a set size for your world, I don't like strict gridding, and I wanted to account for the fact that I might have large objects! But there's a solution, and, as it <em>always</em> seems to be in the world of data structures for practical everyday purposes, it's hash tables.</p>
<p>Here's how my system works: The Spatial Hash Grid is a dict. The keys are (x,y) tuples which index into the infinite space of grid squares. The items are a secondary collision-detection data structure, which contain the objects. To find the key for an object, you just have to integer divide its coordinates. Since it just uses python's built-in dicts, it's hashing the tuples - so you never need to pre-specify the size of your world! The thorniest thing to get around was storing large objects. I tried throwing items into all the grid squares they touch, but it was too slow. The solution I ended up using is to make multiple tiers.</p>
<p>When you put an object in the grid, it checks its size (by looking at the min/max x/y coordinates, which shape objects contain). If it's small enough to fit in this grid - that it will only ever occupy one square and its neighbors - then it goes in the dict we're looking at. Otherwise, the grid makes a new grid - with twice the grid size! Larger objects get shoved up the ladder, and when you're checking for collisions, you traverse up the ladder until you hit the end. This is reasonably fast so long as you don't have unreasonably many objects on different size scales.</p>
<p>And it works! I haven't done extensive testing, but it goes up to 150 - 40 moving around, and the rest stationary - with 60FPS. I've gotten some slowdown after that (only from adding more moving objects) but it's certainly fast enough for my purposes - and some of the speed loss is from unoptimized rendering - and it should be the case that a sparsely populated world will have nearly no slowdown from additional objects being chucked in.</p>
</li>
</ul>
<p>So I'm using Spatial Hashing, and there're ways for it to be sped up, but collision detection works pretty well for now - certainly well enough to focus on other things.</p>
<h3>Yes, but what about... anything else?</h3>
<p>Well, this project is about the engine, so I'm not prepared to go into the actual game I want to make with it, but there's certainly graphics and windowing to talk about. The graphics are, for the moment, all coded up with vertex lists that it generates when it runs, and they're far from optimized or anything. This is good because I learned a lot about OpenGL. This is bad because pretty much all of what I learned is now obsolete, since the computer I've been developing for doesn't... have support... for shaders. Whoops. I want to get more experience with shaders, but I don't have a computer that's good for it right now. Also, that will be more meaningful when I have a better model-&lt;engine pipeline for actually putting content into the engine.</p>
<p>Windowing is, for the moment, done through pyglet, which is a lightweight framework for windowing and opengl bindings. I like it okay, but it's not capable of doing much - I'd have to write my own GUI, which would be suboptimal (although not too hard) given that I could just get a framework to do it for me. What I'm trying to do now is switch to using PyQt, having tried wxPython briefly and found it very hard to work with. I'm still working through reimplementing secondary things that pyglet was doing for me, such as keyboard and mouse handling and clock/fps management. That's what the gui branch is for, on github.</p>
<h3>I guess links would be nice?</h3>
<p>Oh, yes! Right. <a href="http://github.com/shoofle/mindjailengine/">The repository</a> is up on github, and it should work for you if you fork it. You'll need to pip install pyglet, or fork their latest dev version if you're on a mac - there's some kind of unresolved problem with OSX or something. That's one of the reasons I want to switch to a different framework - better portability.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,71 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Projects, by Shoofle</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article class="project">
<a name="auriga"></a>
<h2 class="title">This site!</h2>
<p>I made this site. It's a showcase of the various projects I work on, and basically the cool things I do.</p>
<p>It's served by a <a href="http://flask.pocoo.org">flask-based</a> miniserver that I wrote. It don't do much, but it works!</p>
<p>For the most part the pages themselves are hand-crafted HTML/CSS, like the artisanal coders of old.</p>
<p>A large portion of this attractive layout is due to the excellent work of the folks at <a href="http://twitter.github.io/bootstrap/">bootstrap</a>.</p>
<p><a href="../auriga/">I've been thinking more about how this server should be laid out, because I just kind of slapped it together.</a></p>
</article>
<article class="project">
<a name="lambdamoo"></a>
<h2 class="title">Worldbuilding Experiment #1: In and Out of the LambdaMOO!</h2>
<p>I like MUDs - Multi-User Dungeons. They're cute to mess around in, and make a fun forum for roleplaying and enjoying a world with just text. I hadn't heard of MOOs - MUDs, but Object Oriented! A friend set up a LambdaMOO server for us to play around in, and I quickly got hooked - the distinguishing feature of LambdaMOO is that, being object-oriented, everything in the world (players, rooms, objects, whatever!) is an object and can be scripted by the same basic rules - and so the world is infinitely extensible.</p>
<p>It's not a particularly elegant interface, and the language it uses (MOOCode) is far from pretty - but I was immediately impressed by how easy it was to create complicated behaviors and build your own worlds. So, faced with an empty canvas, I started building. Before I knew it, there were the beginnings of a setting in front of me. <span class="todo">I'm going to write a page about it on its own soon, but I haven't gotten around to it.</span> It was cool, but stale - so I learned the language (didn't take long!) and before I knew it, the rolling plains of my world had a giant creature striding across them! My city was, with just a few short lines, bustling with NPCs wandering around. It was great!</p>
<p>Of course, I was proud of what I had built - but I could only give the same description of the world so many times before I got tired of telling people about this awesome world in my head. Getting people onto the server itself would be too much trouble. I wanted to bring my world out of my friend's server - so I set to work.</p>
<p>Exporting from LambdaMOO isn't easy. Basically your options are: <ul><li>Copy the database.</li></ul> That's it. I figured that, given how simple the data itself is, there had to be <em>some</em> way to show this to people! So I looked around for a bit to find some way to show people a world like this - even if it was just to explore the setting, without the giant creatures or NPCs therein.</p>
<p>Through entirely unrelated means, around this time, I had heard of something called <a href="http://www.gimcrackd.com/etc/src/">Twine</a>. Twine is a program for writing interactive fiction, styled after the old Choose Your Own Adventure books of our childhoods. It's got a nice, easy-to-use GUI so that nontechnical people can write games with this - which is how I heard of it. There's a tremendous number of people who are just writing tiny, fascinating games using Twine, because it's free and you don't need to be a programmer! Fascinating for games as a medium. <span class="todo">I'm going to write some Twine games at some point, but I haven't yet.</span> For me, Twine was great because of the other part of the application - twee.</p>
<p>Twee is the CLI version of Twine, which takes a specially formatted plaintext file and turns it into HTML for the equivalent hypertext game. You write passages, specify links between them, and so on. It takes a simple format. So this is what inspired me:</p>
<p>In the MOO, I described a simple object, using the NPC base class my friend built, that would wander around with specified limitations on where it could go within the world. As it did so, it would write down all the places it had been. As a final step, I defined functionality for this Cartographer to print out for me the places it has been, in the format that twee takes. Copy-and-paste into a file, twee churns it out, and <a href="city-on-the-river">ta-da!</a></p>
<p>You can think what you want of the city I built - and it may be out of date, because I'm continuing to work and only occcasionally run the Cartographer - but I'm proud of it. More than that, I'm proud of the fact that my solution, when faced with the problem of how to get my world out of a server, was to describe an NPC who literally maps out the world, set them at it, and put their map through a converter directly into a webpage. I did a minimum of coding for this, and it's awesome that the tools exist for such an easy-to-explain solution.</p>
</article>
<article class="project">
<a name="shoof-shoof-revolution"></a>
<h2 class="title">Shoof Shoof Revolution!</h2>
<p>As part of Play for X, I made a javascript-powered clone of DDR, using SVG. I like working with SVG and javascript - I can directly manipulate drawing rules and maintain an incredibly tight feedback loop as I develop. In this case, though, I was so proud of what I made that I kept trying to make it better. It's far from finished and I've set it aside as I work on other projects, but, in theory, this project was to make a simple javascript clone of DDR - but using the web audio API, I was going to make it so you could load in a file, entirely locally, and it would do automatic beat detection and generate an actual track for your song. Unfortunately, audio processing and beat detection is less than easy and simple when you're using javascript. <a href="http://github.com/shoofle/shoof-shoof-revolution">It's a cool project, and I'm interested in returning to it sometime.</a></p>
</article>
<article class="project">
<a name="spinning"></a>
<h2 class="title">Spinning!</h2>
<p>I'm into spinning. Here is a list of things I do not mean:</p>
<ul>
<li>group stationary biking</li>
<li>repeatedly turning around (well, sometimes)</li>
<li>hitting the gas with insufficient traction</li>
</ul>
<p>Here is a list of what I do mean:</p>
<ul>
<li>A performance art revolving (HA!) around skilled manipulation of a variety of props, frequently incorporating dance.</li>
</ul>
<p>It's kind of like juggling. It'd be hard to explain juggling without showing - <a href="http://www.youtube.com/watch?v=0smoYMNsW-k#3m10s">but fortunately, we live in the future.</a></p>
<p>I've been spinning poi since 2010, and I'm (I think) reasonably competent. I tend to favor fast circle-spinning over heavy use of stalls and pendulums, and I'm proud of the variety I've developed in my weaves. I can spin a five-beat weave forward, but I'm still working on the reverse. <a href="spinning">This should have a more comprehensive list of my spinning repertoire, because I like listing things.</a> I spin a pair of <a href="http://www.youtube.com/watch?v=0smoYMNsW-k">sol-colored crystal case flowlights</a>.
</p><p>Since the end of 2012 I have also been spinning a staff. I use one that I made from aluminum pipe bought from amazon's hobby supply website. It's got a pair of old blue jeans' legs for end padding, and it's heavy as a bag of bricks but it works. I can do a <a href="spinning">variety of things</a> with it.</p>
<p><span class="todo">I still don't have any videos up, but that should change soon.</span></p>
</article>
<article class="project">
<a name="silver-asterism"></a>
<h2 class="title">I Blog with Friends?</h2>
<p>I occasionally post on <a href="http://silverasterism.blogspot.com/">this game design and discussion blog called Silver Asterism</a> run by a couple friends and myself. It's a pretty sweet thing. We don't post particularly often, but we're all passionate about gaming. We even taught a seminar class for part of a semester about games as art when we were in college together!</p>
</article>
<article class="project">
<a name="guitar"></a>
<h2 class="title">I play a guitar???</h2>
<p>I like music! I'm not particularly <em>good</em> at it, but I like it. I have a guitar, it is tiny and I adore it. Every now and then I pick it up and try to figure out how to get some sound out of it.</p>
<p>I've been collecting tab for songs I find interesting for quite some time now, and I figured I might as well <a href="../guitar_tab/">store them online</a> so that I can pull them up easily:</p>
<ul>
{% for file in guitar_files %}
<li><a href="../guitar_tab/{{ file }}">{{ file|replace("_"," ") }}</a></li>
{% endfor %}
</ul>
</article>
</body>
</html>

View File

@ -0,0 +1,72 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Projects, by Shoofle</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
<style type="text/css"> body { transform: scale(0.5); transform-origin: 0% 0%; } </style>
</head>
<body>
<article class="project">
<a name="auriga"></a>
<h2 class="title">This site!</h2>
<p>I made this site. It's a showcase of the various projects I work on, and basically the cool things I do.</p>
<p>It's served by a <a href="http://flask.pocoo.org">flask-based</a> miniserver that I wrote. It don't do much, but it works!</p>
<p>For the most part the pages themselves are hand-crafted HTML/CSS, like the artisanal coders of old.</p>
<p>A large portion of this attractive layout is due to the excellent work of the folks at <a href="http://twitter.github.io/bootstrap/">bootstrap</a>.</p>
<p><a href="../auriga/">I've been thinking more about how this server should be laid out, because I just kind of slapped it together.</a></p>
</article>
<article class="project">
<a name="lambdamoo"></a>
<h2 class="title">Worldbuilding Experiment #1: In and Out of the LambdaMOO!</h2>
<p>I like MUDs - Multi-User Dungeons. They're cute to mess around in, and make a fun forum for roleplaying and enjoying a world with just text. I hadn't heard of MOOs - MUDs, but Object Oriented! A friend set up a LambdaMOO server for us to play around in, and I quickly got hooked - the distinguishing feature of LambdaMOO is that, being object-oriented, everything in the world (players, rooms, objects, whatever!) is an object and can be scripted by the same basic rules - and so the world is infinitely extensible.</p>
<p>It's not a particularly elegant interface, and the language it uses (MOOCode) is far from pretty - but I was immediately impressed by how easy it was to create complicated behaviors and build your own worlds. So, faced with an empty canvas, I started building. Before I knew it, there were the beginnings of a setting in front of me. <span class="todo">I'm going to write a page about it on its own soon, but I haven't gotten around to it.</span> It was cool, but stale - so I learned the language (didn't take long!) and before I knew it, the rolling plains of my world had a giant creature striding across them! My city was, with just a few short lines, bustling with NPCs wandering around. It was great!</p>
<p>Of course, I was proud of what I had built - but I could only give the same description of the world so many times before I got tired of telling people about this awesome world in my head. Getting people onto the server itself would be too much trouble. I wanted to bring my world out of my friend's server - so I set to work.</p>
<p>Exporting from LambdaMOO isn't easy. Basically your options are: <ul><li>Copy the database.</li></ul> That's it. I figured that, given how simple the data itself is, there had to be <em>some</em> way to show this to people! So I looked around for a bit to find some way to show people a world like this - even if it was just to explore the setting, without the giant creatures or NPCs therein.</p>
<p>Through entirely unrelated means, around this time, I had heard of something called <a href="http://www.gimcrackd.com/etc/src/">Twine</a>. Twine is a program for writing interactive fiction, styled after the old Choose Your Own Adventure books of our childhoods. It's got a nice, easy-to-use GUI so that nontechnical people can write games with this - which is how I heard of it. There's a tremendous number of people who are just writing tiny, fascinating games using Twine, because it's free and you don't need to be a programmer! Fascinating for games as a medium. <span class="todo">I'm going to write some Twine games at some point, but I haven't yet.</span> For me, Twine was great because of the other part of the application - twee.</p>
<p>Twee is the CLI version of Twine, which takes a specially formatted plaintext file and turns it into HTML for the equivalent hypertext game. You write passages, specify links between them, and so on. It takes a simple format. So this is what inspired me:</p>
<p>In the MOO, I described a simple object, using the NPC base class my friend built, that would wander around with specified limitations on where it could go within the world. As it did so, it would write down all the places it had been. As a final step, I defined functionality for this Cartographer to print out for me the places it has been, in the format that twee takes. Copy-and-paste into a file, twee churns it out, and <a href="city-on-the-river">ta-da!</a></p>
<p>You can think what you want of the city I built - and it may be out of date, because I'm continuing to work and only occcasionally run the Cartographer - but I'm proud of it. More than that, I'm proud of the fact that my solution, when faced with the problem of how to get my world out of a server, was to describe an NPC who literally maps out the world, set them at it, and put their map through a converter directly into a webpage. I did a minimum of coding for this, and it's awesome that the tools exist for such an easy-to-explain solution.</p>
</article>
<article class="project">
<a name="shoof-shoof-revolution"></a>
<h2 class="title">Shoof Shoof Revolution!</h2>
<p>As part of Play for X, I made a javascript-powered clone of DDR, using SVG. I like working with SVG and javascript - I can directly manipulate drawing rules and maintain an incredibly tight feedback loop as I develop. In this case, though, I was so proud of what I made that I kept trying to make it better. It's far from finished and I've set it aside as I work on other projects, but, in theory, this project was to make a simple javascript clone of DDR - but using the web audio API, I was going to make it so you could load in a file, entirely locally, and it would do automatic beat detection and generate an actual track for your song. Unfortunately, audio processing and beat detection is less than easy and simple when you're using javascript. <a href="http://github.com/shoofle/shoof-shoof-revolution">It's a cool project, and I'm interested in returning to it sometime.</a></p>
</article>
<article class="project">
<a name="spinning"></a>
<h2 class="title">Spinning!</h2>
<p>I'm into spinning. Here is a list of things I do not mean:</p>
<ul>
<li>group stationary biking</li>
<li>repeatedly turning around (well, sometimes)</li>
<li>hitting the gas with insufficient traction</li>
</ul>
<p>Here is a list of what I do mean:</p>
<ul>
<li>A performance art revolving (HA!) around skilled manipulation of a variety of props, frequently incorporating dance.</li>
</ul>
<p>It's kind of like juggling. It'd be hard to explain juggling without showing - <a href="http://www.youtube.com/watch?v=0smoYMNsW-k#3m10s">but fortunately, we live in the future.</a></p>
<p>I've been spinning poi since 2010, and I'm (I think) reasonably competent. I tend to favor fast circle-spinning over heavy use of stalls and pendulums, and I'm proud of the variety I've developed in my weaves. I can spin a five-beat weave forward, but I'm still working on the reverse. <a href="spinning">This should have a more comprehensive list of my spinning repertoire, because I like listing things.</a> I spin a pair of <a href="http://www.youtube.com/watch?v=0smoYMNsW-k">sol-colored crystal case flowlights</a>.
</p><p>Since the end of 2012 I have also been spinning a staff. I use one that I made from aluminum pipe bought from amazon's hobby supply website. It's got a pair of old blue jeans' legs for end padding, and it's heavy as a bag of bricks but it works. I can do a <a href="spinning">variety of things</a> with it.</p>
<p><span class="todo">I still don't have any videos up, but that should change soon.</span></p>
</article>
<article class="project">
<a name="silver-asterism"></a>
<h2 class="title">I Blog with Friends?</h2>
<p>I occasionally post on <a href="http://silverasterism.blogspot.com/">this game design and discussion blog called Silver Asterism</a> run by a couple friends and myself. It's a pretty sweet thing. We don't post particularly often, but we're all passionate about gaming. We even taught a seminar class for part of a semester about games as art when we were in college together!</p>
</article>
<article class="project">
<a name="guitar"></a>
<h2 class="title">I play a guitar???</h2>
<p>I like music! I'm not particularly <em>good</em> at it, but I like it. I have a guitar, it is tiny and I adore it. Every now and then I pick it up and try to figure out how to get some sound out of it.</p>
<p>I've been collecting tab for songs I find interesting for quite some time now, and I figured I might as well <a href="../guitar_tab/">store them online</a> so that I can pull them up easily:</p>
<ul>
{% for file in guitar_files %}
<li><a href="../guitar_tab/{{ file }}">{{ file|replace("_"," ") }}</a></li>
{% endfor %}
</ul>
</article>
</body>
</html>

View File

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
http://www.nintendo.com/consumer/info/en_na/docs.jsp ?
</article>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h1>OAuth Adventures</h1>
<h4>(how does it work?)</h4>
<p>I recently had some exciting adventures involving trying to make a bot that posted to Tumblr. I eventually got it working (well, so far, just the posting part, but that's the one I didn't understand!)) but decided that I should write down what I learned about OAuth - and specifically, how to use it to write robots that post on their own.</p>
<p>The problem is that all the many guides to using OAuth are specifically geared towards the expected use case - I want my app to access users' data on other services like twitter! That's not what I want. I want my bot to be able to periodically run and access some tumblr accounts. This seems like it shouldn't be too hard, but I don't want to code in the password to this account! So what do I do?</p>
<p>So let's go through this backwards.</p>
<p>It's doable, very doable if you're using a library like oauth2, and extremely doable if you're using <a href="https://github.com/tumblr/pytumblr">tumblr's python api!</a> Either way, you're going to end up making requests to the <a href="http://www.tumblr.com/docs/en/api/v2">tumblr web api</a> using open authentication, and you're going to need a token to do it.</p>
<p>You need an Access Token.</p>
<p>An access token is keyed to your application, and it's been verified by Tumblr that a user has verified that you can use it. Think of it as the key to your user's content, which your user hands over to the application.</p>
<p>Of course, in your case, you're not going to care much about the exact mechanics of acquiring the access token, beyond the first time. You use the access token to sign your requests. How to do that is something I'm sure there are other tutorials for - if you're using oauth2, you simply need to specify it as the token for your client object:</p>
<pre><code>import oauth2 as oauth
client = oauth.Client(consumer, access_token)</pre></code>
<p><code>consumer</code> here refers to a key/secret pair, which you're given when you register your application. It lets tumblr know (through the oauth protocol) which application this is.</p>
<p>The <code>access_token</code> is <em>also</em> a key/secret pair, but you get it when the user logs in through oauth. The key and the secret are two strings of random-looking stuff, which is cryptographic magic.</p>
<p>If you're trying to figure out (as I was) how to get your python script to access user-restricted tumblr data, but you don't want to code the password to your account directly into the script, then the access token is exactly what you're looking for. I stuck it in a file that I don't share with anyone, from which my script imports the keys. Now I could share my script, but no one would have my keys - and even if they did, I could revoke its access to my account at any time.</p>
<p>If I'm using oauth2, then it works as above, and you use the <code>client</code> object to make your requests. If you're using tumblrpy, then you simply provide the consumer token's key and secret, as well as the access token's key and secret, to the library when you are initializing your client object. It's very easy!</p>
<h4>Okay but how do you do it?</h4>
<p>I can tell you're a smart cookie! That's a very good question. It's easy to use the access token, but how do you actually <em>acquire</em> it? As far as I can tell, the answer is to do the standard three-legged authentication process, but only do it once.</p>
<p>I just brought up the interactive python console and started typing in code as though I were trying to code a three-legged authentication workflow (this is the one you hear about when you google for oauth tutorials). Here are the steps of that process, in case you forgot:</p>
<ul>
<li>Acquire a consumer key and secret when you register your application.</li>
<li>Initialize an <code>oauth.Consumer</code> object with the consumer key and secret.</li>
<li>Initialize an <code>oauth.Client</code> object with your <code>oauth.Consumer</code> object.</li>
<li>Using that client, make a request to the request token url (for tumblr, it's <a href="http://tumblr.com/oauth/request_token">http://tumblr.com/oauth/request_token</a>). The response will contain a request token - a key and secret pair. This is used to acquire your access token!</li>
<li>Produce a URL to which to direct the user you want to get credentials for. This looks something like this: <code>http://tumblr.com/oauth/authorize?oauth_token=[your request token's key goes here]</code>.</li>
<li>When a user (such as yourself!) goes there, they will be prompted for what to do, and then when they grant authorization, they'll be redirected somewhere with a "verifier" in the URL arguments. If you were making a standard three-legged application, this would redirect you back to your website, which would then store and use the verifier. You just need to redirect yourself to somewhere and copy/paste the verifier out of your URL bar. The callback URL is set in the tumblr settings for the application.</li>
<li>Now you can use <code>token.set_verifier([some verifier])</code> to set your request token as being verified as good.</li>
<li>Your request token has been verified, so now you simply make a request (using that token, so <code>client = oauth.Client(consumer, request_token)</code>) to the access token URL - for example, <a href="http://tumblr.com/oauth/access_token">http://tumblr.com/oauth/access_token</a>. The response to <em>this</em> will finally contain... Drumroll!</li>
</ul>
<h4>The access token!</h4>
<p>So that's how it's done.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<header>
<h1>Object-Oriented Programming</h1>
</header>
<p>Object-oriented programming means a lot of things to a lot of different people. A lot of people debate or argue about "what object-oriented programming means".</p>
<p>The way I see it, there are two things that make programming object-oriented.</p>
<h3>Part The First</h3>
<p>Suppose I'm writing a system that manages documents. I can access the length of a document by <code>document.length</code>! If I want to capitalize the document, I call <code>document.capitalize()</code>. If I want the title of the document, I simply inspect <code>document.title</code>. This is the essence of object-oriented programming. The alternative to this involves horrible things like manually keeping track of lists of all the attributes of the documents, so you would have <code>titles</code>, <code>body_texts</code>, <code>lengths</code> all as separate arrays, and if you want the title of the first document you ask for <code>titles[0]</code> or something and ANYWAY THE POINT IS: this would be stupid. So that's the essence of objects.<p>
<p>It's the essence of object-oriented programming! But it's not.</p>
<h3>Part The Second</h3>
<p>Because really, what makes object-oriented programming awesome is that an object knows itself. When I call <code>document.capitalize()</code>, then the code that gets executed knows <em>which document to capitalize</em>. That's the important thing!</p>
<h3>Part The Unrelated</h3>
<p>I kind of hate C/C++ and their legacy for overburdening OOP with this confusing question. When the above was suggested to me, I kind of had this moment when I asked</p>
<blockquote>What? Why is that a big deal? Of course <code>document.capitalize()</code> knows which document it's talking about - you're asking for the <code>capitalize</code> function of <em>that specific document</em>. It shouldn't know that other documents exist!</blockquote>
<p>Asking this question was a sign of how I had learned OOP - because in the Olden Times, you were always confronted with the fact that the function <code>document.capitalize()</code> doesn't belong to the specific <em>instance</em> that you called it on, but rather it belongs to the <em>class</em> called "<code>Document</code>" or whatever. And I think, ultimately, that that's a very confusing way for things to work!</p>
<p>Fortunately, it seems like most modern languages (I'm looking at you, javascript and python! Save the day!) take reasonably large strides towards hiding the actual owner of any particular method on an object.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,36 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h1 class="title">Play for X!</h1>
<p>This is a running project I'm working on. Check it out on <a href="http://li60-203.members.linode.com:7777/">another port on this very server</a>, with the caveat that it's not exactly the belle of the ball at the moment. Lately I've not been focusing on the styling and design so much as I've been focusing on...</p>
<p>The functionality! The super short version is this:</p>
<blockquote>
<p>Instead of rolling dice to see if you hit a monster, why not play a minigame instead?</p>
</blockquote>
<p>Tabletop roleplaying games (my experience is mostly with D&amp;D 3.5, but I've also been playing <a href="http://www.dungeon-world.com/">Dungeon World</a> and someday I want to run or be in a <a href="http://www.mimgames.com/window/">The Window</a> game) have the well-agreed on standard that you use dice to generate random numbers to check whether you succeed on chance-based tasks. This introduces instability into the game, which makes things tense and interesting and prevents the game from playing the same way every time!</p>
<p>But the only input the player actually has on how well they do, then, is the gameplay choices which apply various bonuses to their rolls. And that's kinda sucky! Because now that we all have computers, why not do something more interesting: </p>
<blockquote>
<p>When your character faces a skill-based task, you have to face one as well.</p>
</blockquote>
<h3>So what <em>is</em> Play for X anyway?</h3>
<p>It's a chatroom, plus a bunch of tiny javascript games. You and your friends all log into a room together, and you start playing your game over the voice chat system of your choice. When you (as the DM) decree that a player needs to make a skill check, you simply choose a minigame or microtask from the list, and drag it onto their name. It immediately pops up on their screen, and when they've completed it, their score is posted into the chatroom, and you can make a decision based on how well they did!</p>
<p>It's not finished yet. How the various parts of the system talk to each other has changed a bit as I've worked on this - the chat functionality works through websockets, and the games are slated to communicate their results to the client's browser by postMessage passing. This is partially implemented in...</p>
<p>The code! The source is available at <a href="http://github.com/shoofle/play-for-x/">the github project play-for-x</a>, and you're entirely welcome to inspect, criticize, suggest, fork, whatever, as you like. I'd be interested in hearing if you do something with it! Some parts are pretty messy, but the games themselves are fun to make.</p>
<h3>Can I check it out?</h3>
<p>Sure! If you want to just try the games, you should direct your eyeballs or eyeball-equivalents at <a href="http://li60-203.members.linode.com:7777/games/">this lil' website</a>, which lets you try out the minigames I've made, play with their configurations, and so on. It uses a <a href="http://jsoneditoronline.org">nifty json editor widget</a> that someone made.</p>
<hr>
<p>One of my favorite things about working on this project is that it really strips out all the cruft that usually comes with working on game projects - I don't need to worry about the engine, I don't need to worry about rendering engines, I don't need to make lots of textures. I explicitly set a goal of never writing a large game for this project. With that requirement in place, I'm totally free to experiment a <em>lot</em>, and just throw together little javascript games in a few hours. Indeed, most of the games you'll see here took me half an afternoon to write! Experimenting with different forms of gameplay, and exploring exactly that side of the craft, is one of my greatest interests.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,19 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h2 class="title">Shoof Shoof Revolution!</h2>
<p>As part of <a href="/play-for-x/">Play for X</a>, I made a javascript-powered clone of DDR, using SVG. I like working with SVG and javascript - I can directly manipulate drawing rules and maintain an incredibly tight feedback loop as I develop. In this case, though, I was so proud of what I made that I kept trying to make it better. It's far from finished and I've set it aside as I work on other projects, but, in theory, this project was to make a simple javascript clone of DDR - but using the web audio API, I was going to make it so you could load in a file, entirely locally, and it would do automatic beat detection and generate an actual track for your song. Unfortunately, audio processing and beat detection is less than easy and simple when you're using javascript. <a href="http://github.com/shoofle/shoof-shoof-revolution">It's a cool project, and I'm interested in returning to it sometime.</a></p>
</article>
</body>
</html>

View File

@ -0,0 +1,79 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<header>
<h1>Snorlax Evaluation</h1>
<p>(define-by-reference, or: lazy evaluation taken to extremes)</p>
</header>
<p>Okay, so I've got a <code>Bomb</code> entity in my game. It's already got the behavior for flying toward its <code>destination</code>. What if I want to make a <code>SmartBomb</code> that tracks a target even if they move? I might do it something like this:</p>
<pre><code>SmartBomb.on_update = function (timestep) {
// other update behaviors go in this function as well
this.destination = target.position
}</code></pre>
<p>I have to explicitly tell my engine to execute this every frame. I think in terms of this kind of thing already, because I'm a math and physics person. Why can't I define relationships like this more simply? What if, in fact, this was the default way we referred to values? <span class="sidenote span4">It's worth noting that what I've thought of here is essentially just <a href="https://en.wikipedia.org/wiki/Functional_reactive_programming">functional reactive programming</a>. You can go read wikipedia if you want.</span></p>
<pre><code>SmartBomb.destination = target.position</code></pre>
<p>Any time something fetches the value of <code>SmartBomb.destination</code> it'll fetch the value of <code>target.position</code>. Writing it this way, instead of explicitly specifying which variables need to be kept up-to-date with other variables (and when they need to be updated!), means it's easier to give variables interesting dependencies on other values - like this <code>shield</code> which should float a certain distance between the <code>player</code> and the <code>enemy</code>:</p>
<pre><code>shield.position = player.position + shield.distance * unit_vector(enemy.position - player.position)</code></pre>
<p>If and when you <em>do</em> need to store something's value at a specific time, you can do that if you want - for example, this code when a bomb explodes:</p>
<pre><code>explosion_location = current_value(bomb.position)
// or maybe the syntax is like this:
explosion_location = bomb.position.current_value</code></pre>
<p>So far, everything's time-independent. For example, the <code>shield</code> object's <code>position</code> depends only on the positions of other objects.</p>
<p>Why not include time-dependence? A <code>bullet</code> has a <code>position</code> and a <code>velocity</code>, and these are related in a way that doesn't change at any point during gameplay. Why not codify that relationship in a similar way?</p>
<pre><code>bullet.position = bullet.position + time_step * bullet.velocity
// or maybe
bullet.position = bullet.position + bullet.position.time_since_last_calculation * bullet.velocity</code></pre>
<p>Okay, if this seems like impossible magic, I'm sorry. Let's walk through it:</p>
<ol>
<li>The global loop asks for <code>bullet.position</code> in order to know where it should be drawn.</li>
<li><code>bullet.position</code> looks at its cached value and sees that it's out of date.</li>
<li><code>bullet.position</code> subtracts timestamps to find the time step between now and whenever it last got calculated, and remembers this as <code>time_step</code></li>
<li>The time-dependence expression is now evaluated, using the last calculated value for <code>bullet.position</code>, and fetching <code>bullet.velocity</code> as normal.</li>
</ol>
<p>Hell, why not remember the entire history of a variable's values?<span class="sidenote span4">Performance.</span> When I fetch the last calculated value for <code>bullet.position</code>, why not go ahead and make that a graph-crawling reference-calculation just like everything else?<span class="sidenote span4">Reactive time-dependence is something to think about for an interesting mechanic, if nothing else - keeping track of dependencies on history means you could change a value at any point throughout the game's history and see how it changes things... Just like <a href="http://achrongame.com">Achron</a>! And the flaws that make it seem infeasible here wouldn't be a problem in, say, a turn-based time game.</span></p>
<p>Performance is a legitimate worry at that point. If you include the full-on time-dependence, then depending on implementation you might end up following a reference back through every frame the game's been through. This approach would obviously fall apart because every value in your world is going to be at least O(t) to calculate, which sucks - there should be <em>no</em> time-dependent complexity - but there are solutions:</p>
<ul>
<li>Careful design can ensure that your objects aren't too densely interlinked.</li>
<li>If you know you can do things more efficiently in some strenuous case, you can always hard-code it to extract current values and explicitly pass them around whenever you want.</li>
<li>Variables should carry metadata about when they last changed, and when their predecessors and descendents in the graph have changed - so that, in calculating the <code>shield</code>'s <code>position</code>, you don't re-fetch the <code>enemy.position</code>, if you know it hasn't changed.</li>
</ul>
<p>Besides which, we have fast computers, and without running into hard algorithmic complexity problems (collision detection, I'm lookin' at you!) it's not <em>that</em> easy to overload a modern gaming machine. And hey, maybe having declarative time-dependent values is a pipe dream - I don't care. The core idea is still awesome.</p>
<p>Here's an example (with more attention paid to data types) of how I'd come up with a vector that always points to a set offset from the position of <code>bullet</code>.</p>
<pre><code>bullet.position = Wrapper(v(1,2))
offset = v(3,4)
endpoint = bullet.position + offset
print endpoint</code>
<samp>--> WrapperSum(bullet.position, offset)</samp>
<code>print endpoint.current_value</code>
<samp>--> v(4,6)</samp>
<code>bullet.position.current_value = v(-10,0)
print endpoint.current_value</code>
<samp>--> v(-7,4)</samp></pre>
<p>Once I've started thinking and talking about this, I start wondering how much we need imperative game loops at all, for anything other than those troublesome time dependencies and feedback loops. I wonder...</p>
<pre><code>print bullet.contacts</code>
<samp>--> Wrapper(List&lt;Contacts&gt;)</samp>
<code>contact_acceleration = sum(contact.force_to_resolve for contact in bullet.contacts)
bullet.acceleration = contact_acceleration</code></pre>
<p>Assuming that force due to contacts is the only thing present in the world, then the bullet now feels enough acceleration to resolve all the conflicts. I mean, assuming contacts are simple to work with, and they always are. Right? Right?</p>
<p>I'm thinking about this a lot because this kind of referencing is very natural for things like defining interlinks between components in my game engine - I've got conflicting interests:</p>
<ul>
<li>I want to keep my vectors and properties lightweight, so that a <code>bullet</code>'s position should just be a vector, with which I should be able to do math as freely and easily as integers.</li>
<li>But I also want to ensure that the <code>collision component</code>'s position is the same as the <code>bullet</code>'s position! I'd like to do <code>collision_comp.position = bullet.position</code></li>
<li>And then I run into the problem that since I want my vectors as primitive as possible, if I change <code>bullet.position</code> then it's not going to change <code>collision_comp.position</code> because pass-by-value is the dominant paradigm.</li>
</ul>
<p>The solution I've been working with is wrap together <em>sets</em> of related data (as <i>components</i>). The <code>player</code> object has a <code>PositionComponent</code> and a <code>CollisionComponent</code>, and the <code>CollisionComponent</code> stores a reference to the <code>player</code>'s <code>PositionComponent</code> so that it can access it at any time. Actually holding those references constant means that there would be problems if the <code>player.position_component</code> suddenly changed to a different object - but that should never happen. I'm satisfied with this for the reasons I implemented it in the first place, but it doesn't get me the dynamic interlinking I'd like from something more like functional reactive programming.</p>
<p>So I don't know. I would love to have the <em>capacity</em> for functional reactive programming in my game engines, at the least! It's so useful for making bulletproof dynamic interactive systems.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<script type="text/javascript" src="/static/flot/jquery.flot.js"></script>
<script type="text/javascript" src="/static/flot/jquery.flot.resize.js"></script>
<script type="text/javascript" src="/something-or-other/soo.js"></script>
<p>Oh look a graph</p>
<div id="the_graph" style="width:100%;height:200px"></div>
<p>That's the graph</p>
<img src="test.png" />
</article>
</body>
</html>

View File

@ -0,0 +1,11 @@
var data, config;
$(document).ready(function() {
data = [];
for (var i=0; i<10; i++) {
data.push([i, i*i/10]);
}
config = {};
config.yaxis = {max: 10};
config.xaxis = {min: 0, max: 10};
$.plot($('#the_graph'), [data], config);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,64 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h1>Spinning!</h1>
<p>I like to spin. If you youtube "fire spinning", you'll find out what I mean. I haven't yet actually spun fire, but I've been spinning my glowsticks and non-fire props for several years now, and I'm pretty dang good, if I say so myself! I started spinning at the University of Virginia (props to my Brown College spinners, and the short-lived poi club!) but now, living in Somerville, Massachusetts, I spin twice a week at the Medford Spinjam (at Tufts!) and the Boston Spinjam (at MIT). Check out their facebook pages to see when and where they meet, if you want to see me (and a bunch of others!) spinning our hearts out.</p>
<section class="prop" id="staff">
<h2 class="title">Poi!</h2>
<p>Poi was the first prop I learned. Once I managed to do a three-beat weave, I zoned out until my partner poked me that they were tired, and I realized that I was completely pooped, as I'd just spun for three hours without even thinking about it. That was in 2011, and I've been spinning poi off-and-on (and other props!) since then.</p>
<p>I like poi becasue it's relaxing. I can get into it and just spin for hours, and let the prop guide my movements. Gets boring, though!</p>
<p>Here's a list of the tricks I can do. If you don't spin a lot, feel free to ignore this. If you do, I apologize for the ones I have kooky names for.</p>
<ul class="tricks">
<li>In-plane spinning</li>
<li>Turns</li>
<li>Butterfly</li>
<li>Thread the Needle (forward only)</li>
<li>Pac-Man Twist</li>
<li>3-beat Weave</li>
<li>Weave extensions</li>
<li>Wrap-beat weave</li>
<li>5-beat Weave (forward only)</li>
<li>Reels</li>
<li>Chasing the Moon, Chasing the Sun, Moon Chasing Sun</li>
<li>Chasing where the Sun Don't Shine (same time only)</li>
<li>Windmills</li>
<li>Air Wraps</li>
<li>All manner of bicep wraps and leg wraps</li>
<li>Spiral wraps</li>
</ul>
<p>I spin a pair of flowlights with crystal cases. I've got the Sol coloration, and I love them dearly (although they have a tendency to turn off at the wrong times). I'm hoping to get a pair of Spectrum flowlights, or possibly build my own. I also have a pair of tailed poi I need to fix - they're so fun to spin!</p>
<p>Someday I'd like to build a pair of accelerometer-powered glowpoi - they could change color depending on the acceleration they're feeling. When you're spinning in a circle, that's directly dependent on speed - it could make for really nifty effects!</p>
</section>
<section class="prop" id="staff">
<h2 class="title">Staff!</h2>
<p>Since the end of 2012 I've also been very into spinning staff. I use a full-length staff that I made myself for super cheap - it's heavy as a car, but it's balanced alright. I spin a mix of traditional and contact staff, because what else would I do?</p>
<p>I like spinning staff because it's <em>fun</em>. Poi is relaxing, meditative; staff is, to me, much more energetic. I feel like I'm getting to play with this giant whirling toy, whereas with poi I feel like I'm guided by the inertia of the props.</p>
<p>Here's a (partial) list of tricks I can do:</p>
<ul class="tricks">
<li>Rotors</li>
<li>Weaves up to 5 beats (I think)</li>
<li>Half-Steves (right-to-left consistently, shakier on left-to-right)</li>
<li>Halos</li>
<li>Angel Rolls (only right-to-left)</li>
<li>Shoulder-Neck-Shoulder</li>
<li>Neck wraps, shoulder wraps</li>
<li>Fishtails (forward only, right hand only)</li>
<li>Rainbow Stalls</li>
<li>Conveyor Belts</li>
</ul>
<p>I'd like to learn to spin a dragon staff someday.</p>
</section>
</article>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<p>In these SVG files, 10 units of SVG space should correspond to one inch of real world space.</p>
<p>Here are, perhaps, the slices that I need:</p>
<img src="slices.svg"/>
<p>And here, again perhaps, are the slices arranged in some way:</p>
<img src="second_slices.svg"/>
</article>
</body>
</html>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox="0 0 500 500"
preserveAspectRatio="meet"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style type="text/css">
text {
font-size: 10px;
stroke: none !important;
}
use {
stroke: red;
stroke-width: 1px;
}
circle, path {
fill: none;
stroke: red;
stroke-width: 1px;
}
</style>
<use id="spacer" xlink:href="slices.svg#small-spacer"/>
<use id="spacer-with-teeth" xlink:href="slices.svg#spacer-with-teeth"/>
<use id="big-spacer" xlink:href="slices.svg#big-spacer"/>
<g id="spacer-column">
<use xlink:href="#spacer" transform="translate(0,0)"/>
<use xlink:href="#spacer" transform="rotate(60) translate(65,0)"/>
<use xlink:href="#spacer" transform="rotate(60) translate(65,0) rotate(60) translate(65,0)"/>
<use xlink:href="#spacer" transform="rotate(60) translate(65,0) rotate(60) translate(65,0) rotate(-60) translate(65,0)"/>
</g>
</defs>
<g transform="translate(50,50)">
<text y="-40">sixteen small barrel spacers</text>
<g class="barrel-mid">
<use xlink:href="#spacer-column"/>
<use xlink:href="#spacer-column"/>
<use xlink:href="#spacer-column" transform="translate(65,0)"/>
<use xlink:href="#spacer-column" transform="translate(65,0) translate(65,0)"/>
</g>
<g class="barrel-end">
<use xlink:href="#spacer-column" transform="translate(65,0) translate(65,0) translate(65, 0)"/>
</g>
</g>
<g class="barrel-end" transform="translate(50,300)">
<text y="-40">large barrel end spacer</text>
<use xlink:href="#big-spacer" transform="translate(0,0)"/>
<use xlink:href="#big-spacer" transform="translate(0,0) rotate(60) translate(70,0)"/>
<use xlink:href="#big-spacer" transform="translate(70,0)"/>
<use xlink:href="#big-spacer" transform="translate(70,0) rotate(60) translate(70,0)"/>
<use xlink:href="#spacer-with-teeth" transform="translate(140,0)"/>
<use xlink:href="#spacer-with-teeth" transform="translate(140,0) rotate(60) translate(70,0)"/>
<use xlink:href="#spacer-with-teeth" transform="translate(210,0)"/>
<use xlink:href="#spacer-with-teeth" transform="translate(210,0) rotate(60) translate(70,0)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,130 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox="0 0 500 500"
preserveAspectRatio="meet"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style type="text/css">
text {
font-size: 10px;
}
circle, path {
fill: none;
stroke-width: 1px;
}
.barrel-mid {
stroke: black;
}
.barrel-end {
stroke: orange;
}
</style>
<circle id="hole" r="5"/>
<g id="positioned-hole">
<use xlink:href="#hole" transform="translate(-20, 0)"/>
</g>
<g id="hole-set">
<use xlink:href="#positioned-hole" transform="rotate(0)"/>
<use xlink:href="#positioned-hole" transform="rotate(60)"/>
<use xlink:href="#positioned-hole" transform="rotate(120)"/>
<use xlink:href="#positioned-hole" transform="rotate(180)"/>
<use xlink:href="#positioned-hole" transform="rotate(240)"/>
<use xlink:href="#positioned-hole" transform="rotate(300)"/>
</g>
<path id="tooth" d="M -3,0 L -2,4 L 2,4 L 3,0"/>
<g id="positioned-tooth">
<use xlink:href="#tooth" transform="translate(-30) rotate(90)"/>
</g>
<path id="tooth-with-arc" d="
M29.8457,-3.03857
L34,-2
V2
L29.8457, 3.03857
A 30,30 0 0 1 27.3664,12.2914
"/>
<g id="three-teeth">
<use xlink:href="#tooth-with-arc" transform="rotate(0)"/>
<use xlink:href="#tooth-with-arc" transform="rotate(30)"/>
<use xlink:href="#tooth-with-arc" transform="rotate(60)"/>
</g>
<g id="tooth-set">
<use xlink:href="#three-teeth" transform="rotate(0)"/>
<use xlink:href="#three-teeth" transform="rotate(90)"/>
<use xlink:href="#three-teeth" transform="rotate(180)"/>
<use xlink:href="#three-teeth" transform="rotate(270)"/>
</g>
<g id="small-spacer">
<circle r="30"/>
<circle r="5"/>
<use xlink:href="#hole-set"/>
</g>
<g id="big-spacer">
<circle r="34"/>
<use xlink:href="#hole-set"/>
</g>
<g id="spacer-with-teeth">
<use xlink:href="#hole-set"/>
<use xlink:href="#tooth-set"/>
</g>
<g id="barrel-spacer-set">
<use xlink:href="#small-spacer" transform="translate(0,0)"/>
<use xlink:href="#small-spacer" transform="translate(100,0)"/>
<use xlink:href="#small-spacer" transform="translate(200,0)"/>
<use xlink:href="#small-spacer" transform="translate(300,0)"/>
</g>
</defs>
<g transform="translate(50,50)">
<text y="-40">Barrel spacer #1</text>
<use xlink:href="#barrel-spacer-set" class="barrel-mid"/>
</g>
<g transform="translate(50,150)">
<text y="-40">Barrel spacer #2</text>
<use xlink:href="#barrel-spacer-set" class="barrel-mid"/>
</g>
<g transform="translate(50,250)">
<text y="-40">Barrel spacer #3</text>
<use xlink:href="#barrel-spacer-set" class="barrel-mid"/>
</g>
<g transform="translate(50,350)">
<text y="-40">End barrel spacer</text>
<use xlink:href="#barrel-spacer-set" class="barrel-end"/>
</g>
<g transform="translate(50,450)">
<text y="-40">Barrel end with holes</text>
<g class="barrel-end">
<use xlink:href="#big-spacer" transform="translate(0,0)"/>
<use xlink:href="#spacer-with-teeth" transform="translate(100,0)"/>
<use xlink:href="#spacer-with-teeth" transform="translate(200,0)"/>
<use xlink:href="#spacer-with-teeth" transform="translate(300,0)"/>
<use xlink:href="#big-spacer" transform="translate(400,0)"/>
</g>
</g>
<g transform="translate(450,50)">
<use xlink:href="#big-spacer"/>
<use xlink:href="#spacer-with-teeth"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,85 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article class="project">
<h1>Tamari Lattices!</h1>
<p>Tamari lattices are graphs (in the mathematical sense - a set of nodes with connections between them) describing a particular set of operations. I like them because they're pretty!</p>
<figure>
<img src="/tamari/tamari_3.png" alt="The (trivial) Tamari lattice for a three-element tree.">
<figcaption>A (trivial) Tamari lattice, generated by the associations of three elements. <a class="source" href="/tamari/tamari_3.dot">Source file.</a></figcaption>
</figure>
<p>Oh - oh dear. How'd that get there? Okay, that's not a very good example.</p>
<p>There are many ways to generate and think of a Tamari lattice. The way I prefer to think of it is this: Consider some binary operation - a way to use two things to make a new one. You want to combine a bunch. So long as you have three or more elements, there's more than one way to combine them - if you've got <code>a</code>, <code>b</code>, and <code>c</code>, you could combine <code>b</code> and <code>c</code>, and then combine the product of that with <code>a</code>, <em>or</em> you could combine <code>a</code> and <code>b</code>, and then combine that with <code>c</code>. We're going to pretend you can't combine <code>a</code> and <code>c</code> (although maybe that would produce more pictures...). The way we write combining two elements is like this: <code>(a, b)</code>. So that means that the first way of combing <code>a</code>, <code>b</code>, and <code>c</code> is written like so: <code>(a, (b, c))</code>. The second way would be <code>((a, b), c)</code>.</p>
<p>Okay, got it? Good.</p>
<p>The game we play is this: You're allowed to step from one way of combining the elements to another, but only by <i class="keyword">left-association</i>: turning <code>(a, (b, c))</code> into <code>((a, b), c)</code>.</p>
<p>The only other rule is that if you can step from one combination to another, the second one has to be drawn below the first one when you list them all out.</p>
<figure>
<img src="/tamari/tamari_4.png" alt="A Tamari lattice for a four-element tree.">
<figcaption>As above, but generated by a four-element tree. <a class="source" href="/tamari/tamari_4.dot">Source file.</a></figcaption>
</figure>
<span class="sidenote span4">You can also think about it in terms of <a href="http://wikipedia.org/wiki/Tree_rotation">tree rotations</a> - but remember that that's a different tree than the one we're building as the Tamari lattice. Tree rotations are just another way to think of left-association and right-association.</span>
<p>If you look at <a href="http://wikipedia.org/wiki/Tamari_lattice">the wikipedia article on Tamari lattices</a>, you'll see a very pretty image:</p>
<figure>
<img src="http://upload.wikimedia.org/wikipedia/commons/4/46/Tamari_lattice.svg">
<figcaption><a href="http://en.wikipedia.org/wiki/File:Tamari_lattice.svg">http://en.wikipedia.org/wiki/File:Tamari_lattice.svg</a></figcaption>
</figure>
<p>There are a lot of ways to reorganize a Tamari lattice, and it takes some artistic work to make one that really looks good. You can even visualize the same graph as <a href="http://en.wikipedia.org/wiki/Associahedron">a 3D shape called an "associahedron"</a>, but I like it in the simple gridded lattice form above. It reminds me how much beauty there can be in regularity - you can see the grid, but it doesn't look constrained by it. I might get a tattoo of the five-element Tamari lattice someday.</p>
<figure>
<img src="/tamari/tamari_5.png" alt="Tamari lattice for a five-element tree.">
<figcaption>Also the five-element lattice! Compare to the <a href="http://wikipedia.org/wiki/Tamari_lattice">example above, on wikipedia.</a>. <a class="source" href="/tamari/tamari_5.dot">Source file.</a></figcaption>
</figure>
<p>All these graphs with the ovals and the curvy lines were generated by yours truly! I made some python that would take a string representation of an association of a number of elements and convert it into an easily-manipulated memory representation. Then, a few tree rotations spit out all the possible results of left-associating on it, and it was relatively simple to print out a <code>.dot</code> file, parseable by <a href="http://www.graphviz.org/">graphviz</a>, that described the graph. It even labeled the nodes!</p>
<p><code>dot</code> happily converted them into the <code>png</code> files you're looking at. Of course, they don't have the human touch, so they're not organized into beautiful grid lines and 45° angles - but it can be fun to try to mentally reorganize them into a nicer shape. If you want, you can download the <code>.dot</code> source files for any of these, and play around with them in a graph-editing program (such as <a href="https://github.com/jrfonseca/xdot.py">XDot</a>)</p>
<figure>
<img src="/tamari/tamari_6.png" alt="Tamari lattice for a six-element tree.">
<figcaption>It's starting to get out of hand, I think. <a class="source" href="/tamari/tamari_6.dot">Source file.</a></figcaption>
</figure>
<p>Unfortunately, at a certain point I think it's going to get difficult to make these pretty. It turns out there are a lot of ways to associate a larger number of elements! Starting with a six-element tree or string, there are 42 elements (above). With seven, there are 132! Wowie!</p>
<figure>
<img src="/tamari/tamari_7.png" alt="Tamari lattice for a seven-element tree.">
<figcaption>Oh dear. <a class="source" href="/tamari/tamari_7.dot">Source file.</a></figcaption>
</figure>
<p>My server was chugging along trying to generate <code>tamari_8.dot</code> but I started getting messages from linode about going over my CPU quotas, so I canceled it - after it ran for an hour or so, without finishing. I think the seven-element one is messy-looking enough!</p>
<p>You can look at the <a class="source" href="/tamari/tamari.py">python script</a> I used to make this, but it's not particularly pretty - I was bored one day and decided I was going to figure out how to generate these... It's not exactly meant to be extensible. It's got a basic binary tree node class I threw together for working the association rules and a handful of (really ugly) helper functions. I just went through and rewrote it a bit to be nicer - the final output loop may please you if you enjoy <code>set</code>s and list comprehensions:</p>
<pre>
<code class="language-python">current_generation = set()
next_generation = {RootOfAllEvil}
edges = set()
labels = set()
while next_generation: # While there are trees to examine.
# Move to look at the next generation.
current_generation = next_generation
# Clear out the next gen to fill it with the children of this generation.
next_generation = set()
# Ensure there are labels for this generation.
labels = labels | set(label_from_node(parent) for parent in current_generation)
for parent in current_generation:
children = generate_children(parent)
labels = labels | set(label_from_node(child) for child in children)
edges = edges | set(str(parent) + " -> " + str(child) + ";" for child in children)
next_generation = next_generation | children
# Output a dot-formatted stream!
args.output.write(u"digraph tamari {\n")
args.output.write(u"\n".join(labels) + "\n")
args.output.write(u"\n".join(edges))
args.output.write(u"\n}\n")</code></pre>
<p>The full script can be used by running <code class="language-bash">python tamari.py --length 4 | dot -Tpng > output.png</code> to produce a graph. <code>tamari.py</code> will print out to a specified file if you also include a filename: <code class="language-bash">python tamari.py --length 5 output.dot</code></p>
</article>
</body>
</html>

View File

@ -0,0 +1,126 @@
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-l", "--length", help="length of the initial string", type=int, default=5)
parser.add_argument("output", help="file to output to", type=argparse.FileType('w'), nargs="?", default=sys.stdout)
args = parser.parse_args()
node_marker = 'O'
class TreeNode:
"""A binary tree node for the purpose of doing tree manipulations and generating tamari lattices. Not very pretty code."""
def __init__(self,l,v,r):
"""Arguments are: left node, payload, right node."""
self.l = l
self.v = v
self.r = r
def __str__(self):
"""Return the string which would be parsed into this object."""
if self.l is None or self.r is None: return str(self.v)
return node_marker + str(self.l) + str(self.r)
def pretty_string(self):
"""Return a string suitable for a label."""
if self.l is None or self.r is None: return str(self.v)
return "(" + self.l.pretty_string() + "," + self.r.pretty_string() + ")"
def rotate_left(self):
if not leaf(self) and not leaf(self.r):
self.l = TreeNode(self.l, None, self.r.l)
self.r = self.r.r
def rotate_right(self):
if not leaf(self) and not leaf(self.l):
self.r = TreeNode(self.l.r, None, self.r)
self.l = self.l.l
def clone(self):
if leaf(self): return TreeNode(None, self.v, None)
else: return TreeNode(self.l.clone(), self.v, self.r.clone())
def count_nodes(self):
if leaf(self): return 0
else: return self.l.count_nodes() + self.r.count_nodes() + 1
def do_on_nth_node(self, n, f):
"""Call the second argument on the nth node in the tree, going depth first from left to right."""
if leaf(self): return n
remaining = self.l.do_on_nth_node(n,f)
if remaining is 0: return 0
if remaining is 1:
f(self)
return 0
return self.r.do_on_nth_node(remaining-1,f)
def __eq__(self, other):
"""Comparison for sets. Just checks if the string representation is the same."""
return str(self) == str(other)
def __hash__(self):
"""Much like __eq__, this is needed for using this in sets. This just hashes the string representation."""
return hash(str(self))
def nth_node_helper(root, n, f):
"""This is so ugly. I just want to do something on the nth node and return the transformed tree! ... But it works. Note: don't extend this script ever. It should stay in its little awful box."""
root.do_on_nth_node(n, f)
return root
def leaf(node):
return node.l is None or node.r is None
def generate_children(root):
"""Return a set of all the trees produced by rotating left at one point in the tree that is passed in."""
# If there are n nodes in the tree, then there are at most n places to do a rotation.
# I didn't feel like figuring out a more direct method to generate all rotations, so I just did this:
# Try rotating at each child node. Remove all the ones that weren't changed (that is, where it failed).
children = set(nth_node_helper(root.clone(), i, TreeNode.rotate_left) for i in range(root.count_nodes()))
return children - {root} # Return the children, except without the root. We don't care about the root.
def _parse(s):
"""Parse one token of the string passed in, and return a tuple: (the tree we parsed, the remainer that wasn't parsed).
This function recursively calls itself, to parse the whole string. """
if s[0] is node_marker:
first, remainder = _parse(s[1:])
second, secondRemainder = _parse(remainder)
return (TreeNode(first, None, second), secondRemainder)
return (TreeNode(None, s[0], None), s[1:])
def parse(string):
"""Parses a string, assuming the node marker in node_marker, and returns a TreeNode.
This is a wrapper around _parse."""
return _parse(string)[0]
# The string we start with is just a slice of the alphabet, with length specified by the command line argument -l.
root_string = "abcdefghijklmnopqrstuvwxyz"[:args.length]
root_string = "".join( "O" + char for char in root_string )
root_string = "".join( root_string.rsplit("O", 1) )
# helper function for generating node labels
label_from_node = lambda x: "{name} [label=\"{label}\"];".format(name=str(x), label=x.pretty_string())
# Here's the root we start with!
RootOfAllEvil = parse(root_string)
#print(RootOfAllEvil)
current_generation = set()
next_generation = {RootOfAllEvil}
edges = set()
labels = set()
while len(current_generation ^ next_generation): # So long as we haven't reached stability...
# Move to look at the next generation.
#print("current_generation: {}, next_generation: {}".format(current_generation, next_generation))
current_generation = next_generation
# Clear out the next generation so that we can fill it with the children of the now-current generation.
next_generation = set()
#print("current_generation: {}, next_generation: {}".format(current_generation, next_generation))
labels = labels | set(label_from_node(parent) for parent in current_generation)
for parent in current_generation:
children = generate_children(parent)
#print("children of {parent}: {children}".format(parent=parent, children=children))
labels = labels | set(label_from_node(child) for child in children)
edges = edges | set(str(parent) + " -> " + str(child) + ";" for child in children)
next_generation = next_generation | children
args.output.write(u"digraph tamari {\n")
args.output.write(u"\n".join(labels) + "\n")
args.output.write(u"\n".join(edges))
args.output.write(u"\n}\n")

View File

@ -0,0 +1,3 @@
digraph tamari {
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

View File

@ -0,0 +1,3 @@
digraph tamari {
OaObc -> OOabc;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -0,0 +1,7 @@
digraph tamari {
OaObOcd -> OOabOcd;
OOaObcd -> OOOabcd;
OaOObcd -> OOaObcd;
OOabOcd -> OOOabcd;
OaObOcd -> OaOObcd;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,23 @@
digraph tamari {
OOabOcOde -> OOOabcOde;
OOaOObcde -> OOOaObcde;
OaOOObcde -> OOaOObcde;
OaOObcOde -> OaOOObcde;
OOOabOcde -> OOOOabcde;
OaObOcOde -> OaOObcOde;
OOaObcOde -> OOOabcOde;
OaOObcOde -> OOaObcOde;
OOabOOcde -> OOOabOcde;
OaObOcOde -> OOabOcOde;
OaOObOcde -> OaOOObcde;
OOaObOcde -> OOOabOcde;
OOOaObcde -> OOOOabcde;
OaObOOcde -> OaOObOcde;
OaOObOcde -> OOaObOcde;
OaObOcOde -> OaObOOcde;
OaObOOcde -> OOabOOcde;
OOaObOcde -> OOaOObcde;
OOOabcOde -> OOOOabcde;
OOaObcOde -> OOOaObcde;
OOabOcOde -> OOabOOcde;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -0,0 +1,86 @@
digraph tamari {
OOabOOOcdef -> OOOabOOcdef;
OaObOOcdOef -> OOabOOcdOef;
OOOaObcOdef -> OOOOaObcdef;
OOOOabOcdef -> OOOOOabcdef;
OOaOObcdOef -> OOOaOObcdef;
OOOabOcOdef -> OOOOabcOdef;
OOOaOObcdef -> OOOOaObcdef;
OOaObcOOdef -> OOOabcOOdef;
OOOaObOcdef -> OOOaOObcdef;
OaOObOcOdef -> OOaObOcOdef;
OOabOOcOdef -> OOOabOcOdef;
OOOaObcdOef -> OOOOabcdOef;
OOaOObOcdef -> OOOaObOcdef;
OOOaObOcdef -> OOOOabOcdef;
OaObOOcdOef -> OaOObOcdOef;
OOaObOcdOef -> OOOaObOcdef;
OaOObOcdOef -> OaOOObcdOef;
OaObOcOOdef -> OaOObcOOdef;
OaObOcOdOef -> OaObOcOOdef;
OaOObOOcdef -> OaOOObOcdef;
OaOOObcdOef -> OOaOObcdOef;
OaOOObOcdef -> OOaOObOcdef;
OaOObOOcdef -> OOaObOOcdef;
OOaObcOdOef -> OOaObcOOdef;
OaOObOcdOef -> OaOOObOcdef;
OOOOabcOdef -> OOOOOabcdef;
OOOaObcOdef -> OOOOabcOdef;
OOOabcOdOef -> OOOOabcdOef;
OOOOabcdOef -> OOOOOabcdef;
OOaObOcOdef -> OOOabOcOdef;
OaObOOcOdef -> OaOObOcOdef;
OOaObcOdOef -> OOOabcOdOef;
OOaObOcdOef -> OOOabOcdOef;
OaOObcOOdef -> OaOOObcOdef;
OOaOObcOdef -> OOaOOObcdef;
OOabOcOOdef -> OOOabcOOdef;
OaObOcOdOef -> OOabOcOdOef;
OaOObcOOdef -> OOaObcOOdef;
OaObOcOdOef -> OaOObcOdOef;
OaObOOcOdef -> OaObOOOcdef;
OOOOaObcdef -> OOOOOabcdef;
OOOabOOcdef -> OOOOabOcdef;
OOabOOcdOef -> OOOabOcdOef;
OOaOObcdOef -> OOOaObcdOef;
OaObOcOOdef -> OOabOcOOdef;
OaOOObcdOef -> OaOOOObcdef;
OOOabcOOdef -> OOOOabcOdef;
OOOabcOdOef -> OOOabcOOdef;
OOaObOOcdef -> OOOabOOcdef;
OaOObOcOdef -> OaOOObcOdef;
OaOOObOcdef -> OaOOOObcdef;
OaObOOOcdef -> OaOObOOcdef;
OOaObOOcdef -> OOaOObOcdef;
OaObOcOdOef -> OaObOOcdOef;
OOOaObcdOef -> OOOOaObcdef;
OOaOObOcdef -> OOaOOObcdef;
OOaOObcOdef -> OOOaObcOdef;
OaObOOcOdef -> OOabOOcOdef;
OaOOObcOdef -> OaOOOObcdef;
OOabOcOOdef -> OOabOOcOdef;
OaOObOcdOef -> OOaObOcdOef;
OOaObOcOdef -> OOaOObcOdef;
OOOabOcdOef -> OOOOabcdOef;
OOOabOcOdef -> OOOabOOcdef;
OOaObcOOdef -> OOOaObcOdef;
OOabOOcdOef -> OOabOOOcdef;
OaOObOcOdef -> OaOObOOcdef;
OaObOcOOdef -> OaObOOcOdef;
OOabOOcOdef -> OOabOOOcdef;
OaOOOObcdef -> OOaOOObcdef;
OOabOcOdOef -> OOabOcOOdef;
OOOabOcdOef -> OOOOabOcdef;
OaObOOcdOef -> OaObOOOcdef;
OaOObcOdOef -> OaOOObcdOef;
OaOObcOdOef -> OOaObcOdOef;
OOaOOObcdef -> OOOaOObcdef;
OaOObcOdOef -> OaOObcOOdef;
OOaObOcdOef -> OOaOObcdOef;
OOaObOcOdef -> OOaObOOcdef;
OOabOcOdOef -> OOOabcOdOef;
OaOOObcOdef -> OOaOObcOdef;
OOaObcOdOef -> OOOaObcdOef;
OaObOOOcdef -> OOabOOOcdef;
OOabOcOdOef -> OOabOOcdOef;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

View File

@ -0,0 +1,332 @@
digraph tamari {
OOOabOcdOeOfg -> OOOOabcdOeOfg;
OaOObOcdOeOfg -> OaOOObcdOeOfg;
OaOOOObcdeOfg -> OOaOOObcdeOfg;
OOabOOOOcdefg -> OOOabOOOcdefg;
OOaObcOOOdefg -> OOOabcOOOdefg;
OaOObOcdOOefg -> OOaObOcdOOefg;
OOaObOcOdeOfg -> OOOaObOcOdefg;
OaObOOOcdOefg -> OaOObOOcdOefg;
OOabOcOdOOefg -> OOOabcOdOOefg;
OOOabOcOdeOfg -> OOOabOOcdeOfg;
OOaObcOdOOefg -> OOOaObcdOOefg;
OOOabcOdOeOfg -> OOOabcOdOOefg;
OaOOOObOcdefg -> OaOOOOObcdefg;
OaObOcOOdOefg -> OaObOcOOOdefg;
OaOObOcOdeOfg -> OOaObOcOdeOfg;
OOabOcOdOeOfg -> OOabOcOOdeOfg;
OOaOObOcdeOfg -> OOOaObOcdeOfg;
OaOObOcdOeOfg -> OaOObOcdOOefg;
OaObOOcdOeOfg -> OaOObOcdOeOfg;
OOOaObcOdOefg -> OOOaObcOOdefg;
OOabOcOdOOefg -> OOabOOcdOOefg;
OOabOOOcOdefg -> OOabOOOOcdefg;
OaOOObcdOOefg -> OOaOObcdOOefg;
OaOObOcOdOefg -> OOaObOcOdOefg;
OOaObOcOdeOfg -> OOOabOcOdeOfg;
OOabOcOdOOefg -> OOabOcOOdOefg;
OaObOOcOdOefg -> OOabOOcOdOefg;
OOOaOObcOdefg -> OOOaOOObcdefg;
OaObOOcdOeOfg -> OOabOOcdOeOfg;
OOOabcOdOOefg -> OOOabcOOdOefg;
OOOOaObOcdefg -> OOOOOabOcdefg;
OOaObOcOOdefg -> OOOabOcOOdefg;
OaOOObOcOdefg -> OOaOObOcOdefg;
OaOObcOOOdefg -> OaOOObcOOdefg;
OOabOOcdOOefg -> OOabOOOcdOefg;
OOaOOObcdeOfg -> OOOaOOObcdefg;
OaOObOOcdOefg -> OaOObOOOcdefg;
OaOOObcdOeOfg -> OaOOOObcdeOfg;
OaOOOObcOdefg -> OaOOOOObcdefg;
OaOObOOcdeOfg -> OOaObOOcdeOfg;
OaOOObOcdOefg -> OOaOObOcdOefg;
OaObOOcOOdefg -> OaObOOOcOdefg;
OOOaObOcOdefg -> OOOaObOOcdefg;
OOOabcOOOdefg -> OOOOabcOOdefg;
OOOOabcOdeOfg -> OOOOOabcdeOfg;
OaOOOObcdeOfg -> OaOOOOObcdefg;
OaObOOcdOeOfg -> OaObOOOcdeOfg;
OOOabcOOdOefg -> OOOOabcOdOefg;
OaOOObcOdOefg -> OOaOObcOdOefg;
OOOOaObOcdefg -> OOOOaOObcdefg;
OaObOOOOcdefg -> OaOObOOOcdefg;
OOOabOcOdOefg -> OOOabOcOOdefg;
OaOOObOcdeOfg -> OOaOObOcdeOfg;
OOabOcOOdOefg -> OOabOcOOOdefg;
OOOaObcdOeOfg -> OOOaObcdOOefg;
OaOObOcOdOefg -> OaOObOOcdOefg;
OaObOOcOdeOfg -> OaObOOOcOdefg;
OaOOObcOdeOfg -> OaOOOObcdeOfg;
OOabOOcOdOefg -> OOabOOcOOdefg;
OaOObcOOdOefg -> OOaObcOOdOefg;
OaObOcOdOeOfg -> OaOObcOdOeOfg;
OOOabOOcOdefg -> OOOabOOOcdefg;
OaObOOOcdOefg -> OOabOOOcdOefg;
OaObOOcdOeOfg -> OaObOOcdOOefg;
OOaOObOcdeOfg -> OOaOOObcdeOfg;
OOOaObOcdeOfg -> OOOOabOcdeOfg;
OOOOabcOOdefg -> OOOOOabcOdefg;
OaObOOcOdOefg -> OaObOOOcdOefg;
OaOObOOcdeOfg -> OaOOObOcdeOfg;
OaOOObOcdeOfg -> OaOOOObOcdefg;
OOaObOOcdeOfg -> OOOabOOcdeOfg;
OOabOOOcdOefg -> OOOabOOcdOefg;
OOaObcOdOOefg -> OOaObcOOdOefg;
OOOaOObcdeOfg -> OOOOaObcdeOfg;
OOabOOcdOeOfg -> OOOabOcdOeOfg;
OOOaObcdOOefg -> OOOOabcdOOefg;
OOaObOcOdeOfg -> OOaObOOcdeOfg;
OOOOOaObcdefg -> OOOOOOabcdefg;
OOabOcOOOdefg -> OOabOOcOOdefg;
OOOaObOcOdefg -> OOOOabOcOdefg;
OOabOcOOOdefg -> OOOabcOOOdefg;
OOaOObcdOOefg -> OOOaOObcdOefg;
OaOObOOcOdefg -> OaOOObOcOdefg;
OOabOcOdOeOfg -> OOabOcOdOOefg;
OOabOOOcdeOfg -> OOOabOOcdeOfg;
OOaObOcOdOefg -> OOOabOcOdOefg;
OOaObOcOdOefg -> OOaObOcOOdefg;
OOOabOcOOdefg -> OOOOabcOOdefg;
OOaObOOOcdefg -> OOaOObOOcdefg;
OOOaObcOdeOfg -> OOOOabcOdeOfg;
OOabOcOOdOefg -> OOOabcOOdOefg;
OaOObOcOdOefg -> OaOOObcOdOefg;
OaOObOcOOdefg -> OaOObOOcOdefg;
OaObOOcdOOefg -> OaObOOOcdOefg;
OOaOObOcOdefg -> OOaOObOOcdefg;
OOOOabcOdOefg -> OOOOOabcdOefg;
OaObOOcOdeOfg -> OOabOOcOdeOfg;
OOOabOcOdOefg -> OOOabOOcdOefg;
OOaObOOcdOefg -> OOaObOOOcdefg;
OaOObOcdOeOfg -> OOaObOcdOeOfg;
OOOabOOcdOefg -> OOOOabOcdOefg;
OaOObOcdOOefg -> OaOOObcdOOefg;
OOOOaObcdOefg -> OOOOOaObcdefg;
OOOabcOdOeOfg -> OOOOabcdOeOfg;
OaOOObcOOdefg -> OaOOOObcOdefg;
OOOOOabOcdefg -> OOOOOOabcdefg;
OOOabcOdOeOfg -> OOOabcOOdeOfg;
OOOOOabcdeOfg -> OOOOOOabcdefg;
OaOOObcOdOefg -> OaOOOObcdOefg;
OOOOabOcdOefg -> OOOOOabcdOefg;
OOaObOOcdOefg -> OOOabOOcdOefg;
OOabOOcdOeOfg -> OOabOOcdOOefg;
OOaObcOOdOefg -> OOOaObcOdOefg;
OaObOcOOdOefg -> OOabOcOOdOefg;
OOaObOcdOOefg -> OOaOObcdOOefg;
OaOOObcOOdefg -> OOaOObcOOdefg;
OOOabOcOOdefg -> OOOabOOcOdefg;
OOaObOOcdeOfg -> OOOaObOOcdefg;
OOaObOcdOeOfg -> OOaObOcdOOefg;
OaOObOOOcdefg -> OaOOObOOcdefg;
OOOabOOcOdefg -> OOOOabOcOdefg;
OOOaObOcdOefg -> OOOOabOcdOefg;
OOOaObcOOdefg -> OOOOabcOOdefg;
OOOabOcOdeOfg -> OOOOabcOdeOfg;
OaOObcOOdeOfg -> OOaObcOOdeOfg;
OaObOcOOOdefg -> OOabOcOOOdefg;
OaOObcOdOeOfg -> OaOObcOOdeOfg;
OOOaObcdOeOfg -> OOOOaObcdeOfg;
OOOOaObcdeOfg -> OOOOOabcdeOfg;
OOOOabOcOdefg -> OOOOOabcOdefg;
OOOabcOdOOefg -> OOOOabcdOOefg;
OOabOOcOdOefg -> OOOabOcOdOefg;
OOaOOObOcdefg -> OOOaOObOcdefg;
OOOOabOOcdefg -> OOOOOabOcdefg;
OOOOabcdOeOfg -> OOOOabcdOOefg;
OOaObcOOdeOfg -> OOOabcOOdeOfg;
OOOOabOcdOefg -> OOOOOabOcdefg;
OaOObOcOOdefg -> OOaObOcOOdefg;
OaOOObcdOOefg -> OaOOOObcdOefg;
OaOOObcdOeOfg -> OaOOObcdOOefg;
OaOOObcOdeOfg -> OOaOObcOdeOfg;
OOaOObcdOeOfg -> OOOaObcdOeOfg;
OOOabOcdOeOfg -> OOOOabOcdeOfg;
OOaObOOcdeOfg -> OOaOObOcdeOfg;
OOaObOcOOdefg -> OOaOObcOOdefg;
OaOObOcOdeOfg -> OaOOObOcOdefg;
OaOOObcOdOefg -> OaOOObcOOdefg;
OaObOOOcdeOfg -> OOabOOOcdeOfg;
OaOObOOcdOefg -> OaOOObOcdOefg;
OOOabOcdOeOfg -> OOOabOcdOOefg;
OOaOObOcdOefg -> OOaOOObcdOefg;
OaObOOcOOdefg -> OaOObOcOOdefg;
OaObOOcOOdefg -> OOabOOcOOdefg;
OOabOOOcdOefg -> OOabOOOOcdefg;
OOaObOcOdOefg -> OOaObOOcdOefg;
OOabOOOcdeOfg -> OOabOOOOcdefg;
OOOabOcOdeOfg -> OOOOabOcOdefg;
OaOObOcOOdefg -> OaOOObcOOdefg;
OOaOObOOcdefg -> OOOaObOOcdefg;
OOaObOcdOOefg -> OOOaObOcdOefg;
OOaOObOOcdefg -> OOaOOObOcdefg;
OaObOOOcdeOfg -> OaOObOOcdeOfg;
OOOabOOcdeOfg -> OOOOabOOcdefg;
OOOOabOcdeOfg -> OOOOOabcdeOfg;
OOaObcOOdOefg -> OOOabcOOdOefg;
OOaObcOOOdefg -> OOOaObcOOdefg;
OOOaObcOdeOfg -> OOOOaObcdeOfg;
OOaOObcOdOefg -> OOOaObcOdOefg;
OOaObOOcOdefg -> OOOabOOcOdefg;
OOOaObOOcdefg -> OOOOabOOcdefg;
OOOabOOcdOefg -> OOOabOOOcdefg;
OOOaObOcdOefg -> OOOaOObcdOefg;
OOaObOcdOeOfg -> OOOaObOcdeOfg;
OOaOOObOcdefg -> OOaOOOObcdefg;
OaOOObOcdOefg -> OaOOOObcdOefg;
OOOaOObcOdefg -> OOOOaObcOdefg;
OaOObOOcdOefg -> OOaObOOcdOefg;
OaOObOOcdeOfg -> OaOOObOOcdefg;
OaObOOcOdOefg -> OaOObOcOdOefg;
OaObOOcdOOefg -> OaOObOcdOOefg;
OaOOOObcdOefg -> OaOOOOObcdefg;
OOaOObcOdOefg -> OOaOOObcdOefg;
OaOObcOOOdefg -> OOaObcOOOdefg;
OaOObcOdOOefg -> OaOOObcdOOefg;
OOabOOcOdeOfg -> OOabOOOcdeOfg;
OaObOOOOcdefg -> OOabOOOOcdefg;
OaOOObOOcdefg -> OaOOOObOcdefg;
OOaOObcOdeOfg -> OOOaObcOdeOfg;
OaOOOObcOdefg -> OOaOOObcOdefg;
OOaOOObcOdefg -> OOaOOOObcdefg;
OaObOcOOdeOfg -> OaObOcOOOdefg;
OOOOOabcOdefg -> OOOOOOabcdefg;
OaObOcOOdeOfg -> OOabOcOOdeOfg;
OOaObcOOdeOfg -> OOaObcOOOdefg;
OaObOOcdOOefg -> OOabOOcdOOefg;
OOOOaOObcdefg -> OOOOOaObcdefg;
OaOObOcOdOefg -> OaOObOcOOdefg;
OOabOcOOdeOfg -> OOabOcOOOdefg;
OOaOOObcdOefg -> OOOaOObcdOefg;
OOOabOcdOOefg -> OOOOabcdOOefg;
OaObOOcOdOefg -> OaObOOcOOdefg;
OaOOObOOcdefg -> OOaOObOOcdefg;
OOabOcOdOeOfg -> OOOabcOdOeOfg;
OaOObcOdOOefg -> OaOObcOOdOefg;
OOOabcOOdOefg -> OOOabcOOOdefg;
OaOOObcdOeOfg -> OOaOObcdOeOfg;
OaOOObOcOdefg -> OaOOOObcOdefg;
OOaObcOdOeOfg -> OOaObcOdOOefg;
OOaOObcdOOefg -> OOOaObcdOOefg;
OaObOcOdOOefg -> OOabOcOdOOefg;
OaOObOOcOdefg -> OaOObOOOcdefg;
OOabOOcdOeOfg -> OOabOOOcdeOfg;
OaObOOOcOdefg -> OaObOOOOcdefg;
OOaOObcOdeOfg -> OOaOOObcdeOfg;
OOOOabcOdeOfg -> OOOOOabcOdefg;
OOOaObcOdeOfg -> OOOOaObcOdefg;
OOaObOOcOdefg -> OOaOObOcOdefg;
OOaObOOcOdefg -> OOaObOOOcdefg;
OOabOcOOdOefg -> OOabOOcOdOefg;
OaOObcOdOeOfg -> OOaObcOdOeOfg;
OaObOOOcOdefg -> OOabOOOcOdefg;
OaObOcOdOeOfg -> OOabOcOdOeOfg;
OaOObcOOdeOfg -> OaOOObcOdeOfg;
OOOaObOcdeOfg -> OOOOaObOcdefg;
OaObOcOdOeOfg -> OaObOOcdOeOfg;
OOOaOObcdeOfg -> OOOOaOObcdefg;
OOOabcOOdeOfg -> OOOabcOOOdefg;
OOaOObOcdeOfg -> OOOaOObOcdefg;
OaObOcOOdeOfg -> OaOObcOOdeOfg;
OOaObOcOOdefg -> OOaObOOcOdefg;
OOOOabcdOOefg -> OOOOOabcdOefg;
OOaOOObcdeOfg -> OOOaOObcdeOfg;
OaOOObOcOdefg -> OaOOObOOcdefg;
OOOOabcOdOefg -> OOOOabcOOdefg;
OOaObOcOdOefg -> OOaOObcOdOefg;
OOOOabOcOdefg -> OOOOabOOcdefg;
OOaObOcdOeOfg -> OOaOObcdOeOfg;
OaOOObcOdeOfg -> OaOOOObcOdefg;
OaObOOcOdeOfg -> OaOObOcOdeOfg;
OOabOOcOdeOfg -> OOabOOOcOdefg;
OOOOaObcdeOfg -> OOOOOaObcdefg;
OOaOObcOdeOfg -> OOOaOObcOdefg;
OOaObOOOcdefg -> OOOabOOOcdefg;
OOabOcOdOeOfg -> OOabOOcdOeOfg;
OOOaObcOdOefg -> OOOOabcOdOefg;
OOabOOcdOOefg -> OOOabOcdOOefg;
OOOaOObOcdefg -> OOOOaObOcdefg;
OOabOOOcOdefg -> OOOabOOcOdefg;
OOaOObcdOeOfg -> OOaOObcdOOefg;
OaObOcOdOOefg -> OaOObcOdOOefg;
OOaOOObcOdefg -> OOOaOObcOdefg;
OaOOObOcdOefg -> OaOOOObOcdefg;
OOOaOObOcdefg -> OOOaOOObcdefg;
OaObOOOcdOefg -> OaObOOOOcdefg;
OOaObcOdOeOfg -> OOOabcOdOeOfg;
OaObOcOOdeOfg -> OaObOOcOdeOfg;
OaOObcOOdOefg -> OaOObcOOOdefg;
OOabOcOOdeOfg -> OOabOOcOdeOfg;
OOaObcOdOeOfg -> OOOaObcdOeOfg;
OOOabOcOdOefg -> OOOOabcOdOefg;
OOaOObcOdOefg -> OOaOObcOOdefg;
OOaObcOOdOefg -> OOaObcOOOdefg;
OOabOcOOdeOfg -> OOOabcOOdeOfg;
OaOOOObcdOefg -> OOaOOObcdOefg;
OOaOObOcdOefg -> OOOaObOcdOefg;
OOaOOOObcdefg -> OOOaOOObcdefg;
OOOOabcdOeOfg -> OOOOOabcdeOfg;
OaObOOcOdeOfg -> OaObOOOcdeOfg;
OaOObcOOdOefg -> OaOOObcOdOefg;
OOOaObOcdOefg -> OOOOaObOcdefg;
OOOaObcOdOefg -> OOOOaObcdOefg;
OOOabOOcdeOfg -> OOOOabOcdeOfg;
OOOOOabcdOefg -> OOOOOOabcdefg;
OaOObcOOdeOfg -> OaOObcOOOdefg;
OaOObcOdOeOfg -> OaOObcOdOOefg;
OOOabOcdOOefg -> OOOOabOcdOefg;
OOOOaObcOdefg -> OOOOOaObcdefg;
OOaObcOdOOefg -> OOOabcOdOOefg;
OOOabcOOdeOfg -> OOOOabcOdeOfg;
OaOOOOObcdefg -> OOaOOOObcdefg;
OOabOOcOOdefg -> OOOabOcOOdefg;
OaOObOcdOOefg -> OaOOObOcdOefg;
OaOObOcOdeOfg -> OaOOObcOdeOfg;
OOOaOObcdOefg -> OOOOaOObcdefg;
OaOObcOdOOefg -> OOaObcOdOOefg;
OaOObOOcOdefg -> OOaObOOcOdefg;
OaObOcOdOeOfg -> OaObOcOdOOefg;
OaObOcOOdOefg -> OaOObcOOdOefg;
OOabOOcOdOefg -> OOabOOOcdOefg;
OOOaOObcdOefg -> OOOOaObcdOefg;
OaObOcOOOdefg -> OaOObcOOOdefg;
OOOOabOcdeOfg -> OOOOOabOcdefg;
OOaObcOOdeOfg -> OOOaObcOdeOfg;
OaOOOObOcdefg -> OOaOOObOcdefg;
OOaOOObcdOefg -> OOaOOOObcdefg;
OaOOObOcdeOfg -> OaOOOObcdeOfg;
OOaOObOcOdefg -> OOOaObOcOdefg;
OOaObOcOdeOfg -> OOaOObcOdeOfg;
OaObOcOdOOefg -> OaObOcOOdOefg;
OaObOcOdOOefg -> OaObOOcdOOefg;
OaObOcOdOeOfg -> OaObOcOOdeOfg;
OaObOcOOOdefg -> OaObOOcOOdefg;
OaObOOOcdeOfg -> OaObOOOOcdefg;
OOaObOOcdOefg -> OOaOObOcdOefg;
OOaOObOcOdefg -> OOaOOObcOdefg;
OOabOOcOdeOfg -> OOOabOcOdeOfg;
OOOaObcdOeOfg -> OOOOabcdOeOfg;
OOOaObOcdeOfg -> OOOaOObcdeOfg;
OOaOObOcdOefg -> OOaOOObOcdefg;
OOabOOcOOdefg -> OOabOOOcOdefg;
OOOaObOOcdefg -> OOOaOObOcdefg;
OOaOObcOOdefg -> OOOaObcOOdefg;
OOOaObcdOOefg -> OOOOaObcdOefg;
OaObOcOOdOefg -> OaObOOcOdOefg;
OaOObOcdOeOfg -> OaOOObOcdeOfg;
OOaObOcdOeOfg -> OOOabOcdOeOfg;
OOaObOcdOOefg -> OOOabOcdOOefg;
OOOOaObcdOefg -> OOOOOabcdOefg;
OaOObOcOdeOfg -> OaOObOOcdeOfg;
OOOaOOObcdefg -> OOOOaOObcdefg;
OaObOOOcOdefg -> OaOObOOcOdefg;
OaOObcOdOeOfg -> OaOOObcdOeOfg;
OOOaObcOOdefg -> OOOOaObcOdefg;
OOaOObcOOdefg -> OOaOOObcOdefg;
OOaObcOdOeOfg -> OOaObcOOdeOfg;
OOOabOOOcdefg -> OOOOabOOcdefg;
OaOObOOOcdefg -> OOaObOOOcdefg;
OOOaObOcOdefg -> OOOaOObcOdefg;
OOaOObcdOeOfg -> OOOaOObcdeOfg;
OOOOaObcOdefg -> OOOOOabcOdefg;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

View File

@ -0,0 +1,97 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article id="tattoo_elements">
<style type="text/css">
#tattoo_elements li { margin-bottom: 1em; }
</style>
<p><a href="http://chemistry.bd.psu.edu/jircitano/periodic4.html">thanks to this site for the images!</a> feel free to suggest other good elements from there.</p>
<ul>
<li><img src="http://chemistry.bd.psu.edu/jircitano/Au.gif" />Gold
<li><img src="http://chemistry.bd.psu.edu/jircitano/Pt.gif" />Platinum
<li><img src="http://chemistry.bd.psu.edu/jircitano/Ag.gif" />Silver
<li><img src="http://chemistry.bd.psu.edu/jircitano/He.gif" />Helium
<li><img src="http://chemistry.bd.psu.edu/jircitano/Li.gif" />Lithium
<li><img src="http://chemistry.bd.psu.edu/jircitano/Be.gif" />Beryllium
<li><img src="http://chemistry.bd.psu.edu/jircitano/Pb.gif" />Lead
<li><img src="http://chemistry.bd.psu.edu/jircitano/Cd.gif" />Cadmium
<li><img src="http://chemistry.bd.psu.edu/jircitano/Si.gif" />Silicon
<li><img src="http://chemistry.bd.psu.edu/jircitano/Ca.gif" />Calcium
</ul>
<p>for use on the results of <a href="http://physics.nist.gov/PhysRefData/ASD/lines_form.html">this atomic spectra database</a>:</p>
<p>CSS:</p>
<pre>
.ac {
background-color: red;
display: inline-block;
width: 1px;
height: 200px;
}
</pre>
<p>javurscript:</p>
<pre>
var jq = document.createElement('script');
jq.src = "http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);
</pre>
<p>wait a moment and more javascript:</p>
<pre>
jQuery.noConflict();
$ = jQuery;
var the_table = $($('table')[4]);
function do_stuff(start, end, intensity_cutoff) {
var wavelengths = [];
$(the_table.find('tbody')[1]).find('tr').each(function() {
if ($(this).hasClass('evn') || $(this).hasClass('odd')) { $(this).show(); }
else { $(this).hide(); return true; }
var wavelength_cell = $($(this).find('td')[0]);
var wavelength = parseFloat(wavelength_cell.text());
if (wavelength &gt;= start && wavelength &lt;= end) { $(this).show(); }
else { $(this).hide(); return true;}
var intensity_cell = $($(this).find('td')[2]);
var intensity = parseInt(intensity_cell.text(), 10);
if (intensity &gt;= intensity_cutoff) { $(this).show(); }
else { $(this).hide(); return true;}
wavelengths.push((wavelength - start)/(end - start));
});
return wavelengths;
}
the_table.after($('&lt;div id="shoof-thing"&gt;&lt;/div&gt;').attr('height', '100px').attr('width', '100%'));
var c = $('#shoof-thing')
function new_thing(x) { return $('&lt;div&gt;&lt;/div&gt;').addClass('ac').css('margin-left', x); }
var w = do_stuff(380, 760, 9);
var l = w.length;
var o = [];
$.each(w, function(i, v) {
if (i == 0) { o.push("" + (v*95) + "%"); }
else { o.push("" + ((v-w[i-1])*95) + "%"); }
});
c.children().remove();
$.each(o, function(i, v) { c.append(new_thing(v)); });
</pre>
</article>
</body>
</html>

View File

@ -0,0 +1,222 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<p>I've been playing with an exciting feature of HTML5 that I hadn't heard of: the <code class="language-html">contentEditable</code> attribute. It's magical! It just makes anything suddenly be editable in-browser! It also finally provides me with the thing I've been wanting ever since growing dissatisfied with text editors: A way to build a <em>new</em> text editor, <em>without</em> having to actually code up the guts of text movement, manipulation, and entry. Oh, happy day!</p>
<p>More on this as the situation progresses.</p>
<pre><code class="language-grammar">
/*
* This is my parser.
* It's not much, but it's mine.
*/
blocks = code:(if_block / lines)* { return code; }
if_block =
"if" ws "(" cond:string ")" ws "{" result:lines "}"
{ return {type: "if", condition: cond, body: result }; }
/*
while_block = "while" ws "(" cond:string ")" ws "{" result:lines "}"
{ return {type: "while", condition: cond, body: result }; }
*/
lines = lines:line+ ws?
{ return lines; }
line = ws? contents:string ";"
{ return contents; }
ws = (" " / "\n")+ { return ""; }
string = characters:([A-Za-z] [0-9A-Za-z "']*)
{ return characters[0] + characters[1].join(""); }
</code></pre>
<p>Then I started realizing I had no idea what I was doing. So I started defining the grammar for a Lisp! Sort of. More or less.</p>
<pre><code class="language-grammar">
expression =
parenthetical
/ bracketed
/ identifier
/ string
/ number
parenthetical =
"(" cons: expression? cdr:(" "+ expression)* ")"
{
var output = [];
for (var i=0; i&lt;cdr.length; i++) {
output.push(cdr[i][1]);
}
return {type: "parenthetical", first: cons, rest: output};
}
bracketed =
"[" cons: expression? cdr: (" "+ expression)* "]"
{
var output = [cons];
for (var i=0; i&lt;cdr.length; i++) {
output.push(cdr[i][1]);
}
return output;
return {type: "list", contents: output};
}
identifier = contents:[A-Za-z]+ { return contents.join(""); }
string =
'"' contents:[^"]* '"' { return contents.join(""); }
/ "'" contents:[^']* "'" { return contents.join(""); }
number = contents:[0-9]+ { return parseInt(contents.join(""), 10); }
</code></pre>
<p>Perhaps the answer is to break up the editor's coding space by lines, as webkit is wont to do. Specifically, this would be good because it'd make it easier to track the cursor position, because it would never change relative to the line element it's contained in!</p>
<style type="text/css">
#editor div {
margin-left: 1em;
}
.block>code.line {
display: block;
margin-left: 1em;
}
.if>.branch {
display: inline-block;
}
</style>
<div id="editor">
<div class="block">
<code class="line">// a comment!</code>
<code class="line">var a = 5;</code>
<code class="line">var b = foo(a); </code>
<div class="if block">
<div class="if branch">
<div class="condition">
if (<code class="line">b &gt; 100 &amp;&amp; b / 7 == 3</code>)
</div>
<div class="block">
{
<code class="line">// Some results should take place</code>
<code class="line">b += 294800;</code>
}
</div>
</div>
<div class="elseif branch">
<div class="condition">
elseif (<code class="line">b &lt; 20</code>)
</div>
<div class="block">
{
<code class="line">b -= 1;</code>
}
</div>
</div>
<div class="else branch">
<div class="condition">
else
</div>
<div class="block">
{
<code class="line">raise UgnaughtException();</code>
}
</div>
</div>
</div>
<code class="line">// So yeah</code>
</div>
</div>
<p>I just keep writing grammars! Thanks, <a href="http://pegjs.majda.cz/online">pegjs!</a></p>
<pre><code class="language-grammar">
block = "{" contents:lines "}" { return contents; }
lines =
contents:(whitespace (branch / line) whitespace)*
{ var output = [];
for (var i=0; i&lt;contents.length; i++) { output.push(contents[i][1]); }
return output; }
branch =
main:if_branch elseifs:elseif_branch* elses:else_branch?
{ var output = [main];
if (elseifs.length &gt; 0) { output = output.concat(elseifs); }
if (elses) { output.push(elses); }
return output; }
if_branch = whitespace cond:if_line whitespace body:block
{ return {type: "if", condition: cond, consequent: body}; }
if_line = "if" whitespace "(" condition:line ")" { return condition; }
elseif_branch = whitespace cond:elseif_line whitespace body:block
{ return {type: "else if", condition: cond, consequent: body}; }
elseif_line = "else if" whitespace "(" condition:line ")" { return condition; }
else_branch = whitespace cond:else_line whitespace body:block
{ return {type: "else", condition: cond, consequent: body}; }
else_line = "else" { return "else"; }
line = start:word rest:(" "* word)*
{ var output = start;
for (var i=0; i&lt;rest.length; i++) { output += rest[i][0].join("") + rest[i][1]; }
return output; }
word = c:[A-Za-z]+ { return c.join(""); }
whitespace = (" " / "\n")*
</code></pre>
<p>Okay, and here's another lisp one, because they're so easy and satisfying:</p>
<pre><code class="language-grammar">
expression = whitespace e:(parenthetical / string_literal / number / identifier) whitespace
{ return e; }
parenthetical = "(" sequence:expression* ")" { return sequence; }
string_literal =
"'" characters:[^']* "'" { return characters.join(""); }
/ '"' characters:[^"]* '"' { return characters.join(""); }
number = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
identifier = letters:[A-Za-z]+ { return letters.join(""); }
whitespace = [ \n\t]*
</code></pre>
<p>So, it nearly killed me, but I managed to figure out how to make a parser (using pegjs) that <em>actually parses indentation-based grammars!</em> The basic way it works is that as it matches a line, it reads in the number of spaces before it. Using that, it figures out what indentation level it's at - then, as it's returning the line's payload, it puts the payload into the right code block. It's hacky and awful but I think I understand how it works. I'd rather move the logic into the <code>block</code> rule rather than the <code>line</code> rule, but that's pretty simple in theory. Here you go:
<pre><code>
{ var code = [];
var indentations = [0];
var current_head = [code]; }
b = line* { return code; }
line =
(spaces:" "* &amp;
{ var level = spaces.length;
if (level &gt; indentations[indentations.length-1]) {
indentations.push(level);
var new_block = [];
current_head[current_head.length-1].push(new_block);
current_head.push(new_block)
} else if (indentations.indexOf(level) == -1) {
return false;
} else {
while (indentations[indentations.length-1] != level) {
indentations.pop()
current_head.pop()
}
}
return true; }
{ return spaces.length; })
payload:("ab" bs:"b"+ "a" { return "ab" + bs.join("") + "a"; })
"\n"
{ current_head[current_head.length-1].push(payload);
return payload; }
</code></pre>
</article>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<header>
<h1>Time Integration</h1>
</header>
<p>I find myself writing cooldowns and countdowns all the time. Bullets can only fire so fast, bombs explode after five seconds, enemies spawn every 10-15 seconds. Every time I do it, I write the same boilerplate in the initialization:</p>
<pre><code>goomba.cooldown_max = 5 // 5 seconds to cool down
goomba.cooldown_timer = goomba.cooldown_max</code></pre>
<p>and the same boilerplate in the update function:</p>
<pre><code>if goomba.cooldown_timer &gt; 0:
goomba.cooldown_timer -= time_step
if goomba.cooldown_timer &lt; 0:
// insert end behavior here</code></pre>
<p>and somewhere, I activate this behavior (in response to a keypress, or proximit, or contact):</p>
<pre><code>goomba.cooldown_timer = goomba.cooldown_max</code></pre>
<p>I do this kind of thing so often that I feel like it should be one of the basic functionalities offered when I'm trying to describe game entity behavior.</p>
<pre><code>// initialization
goomba.shot_timer = new CooldownTimer(length=5s, on_end=function() {...})
// update functionality should be taken care of automatically, but if not:
goomba.shot_timer.step(time_step)
// activating the timer:
goomba.shot_timer.start()</code></pre>
<p>Now, wasn't that easy?</p>
<p>Of course, that's a pretty simple case! There are obvious extensions, though:</p>
<pre><code>goomba.blink_timer = new EndlessTimer()
goomba.blink_timer.interval = 5s
goomba.blink_timer.at_progress(0%, goomba.change_color) // change color at the start of each interval
goomba.blink_timer.at_progress(50%, goomba.take_step) // take a step once every interval halfway through</code></pre>
<p>And now making timed behavior for our goomba is easy! We can even speed up the goomba easily: <code>goomba.blink_timer.interval = 3s</code></p>
<p>Ideally, you don't need to even specify when to <code>step</code> your timers - that should be taken care of automatically. Of course, that means we'd want to <em>register</em> these timers instead of simply declaring them as a member - but maybe that's a matter of a grander architectural problem.</p>
<p>Although! If we <em>do</em> let the developer take care of stepping the timers, then we can also use it for things that <em>aren't</em> time-dependent:</p>
<pre><code>player.limit_bar = new Meter(length=100) // general verison of the CooldownTimer
player.on('hit by enemy', player.limit_bar.step)
player.limit_bar.at_progress(100%, player.limit_break)</code></pre>
<p>And now we've got a way for limit breaks to engage. Assuming there's a pre-existing event system and everything is architected carefully, it's now incredibly easy to fill meters in response to events.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<h1>Indent Adventures</h1>
<p>Look, I know it's wrong. I know I'm not supposed to do inline javascript. But I just want to put everything together! I want code to be easily accessible from the <em>single</em> place that it's relevant! That's <em>important</em> to me. Rest assured: Once my projects get large, I start pulling them apart. But in the mean time, the code I write (being generally game-related as it is) is extremely specific to the presentation, and I think it's actually inappropriate (not to mention inefficient, from my point of view) to separate them widely. There's just one problem:</p>
<p>Vim sucks at autoindenting.</p>
<p>Augh, this whole morning has been essentially wasted, because I spent it trying - <em>trying my hardest</em> - to find a vim indenting plugin that will indent javascript inline in HTML. It turns out that, as far as I can find, <em>no such thing exists.</em> There are a million wonderful javascript indenters, which work beautifully, but which only work on files that are javascript through-and-through. Unfortunately, my heart is set on keeping my javascript intertwined lovingly with my HTML, until such time as I feel like I should stop doing that.</p>
<p>So I had to fix it myself. <em>The pain.</em> It turns out vim scripting is, like the rest of this horrible death-train of an editor, obtuse, stuck in the past, and hard to work with. I guess I'll give thanks for the fact that I was able to edit it at all... But I feel like that's kind of a basic thing for an extensible editor. Anyway.</p>
<p>I got a copy of the HTML indent rules off of a Gary Bernhardt bitbucket link I found from my googling all morning, and delved into it. It turns out that the rules for indenting and dedenting on lines with bracket are only dependent on whether or not the lines in question contain open and close braces - and not <em>how many of those there are.</em> The practical effect of this: <code class="lang-js">function () {};</code> is dedented the same way <code class="lang-js">};</code> is. <strong>What?</strong></p>
<p>It's not even hard to fix (<a href="/static/html.vim">and I have managed to</a>) - all you need to do is count the braces and do some subtraction. For every brace closed on this line which isn't also opened on this line, dedent this line. For every brace opened on the previous line which isn't closed on the previous line, indent this line. <em>That's it.</em> As of this writing, my indent script has an unfortunate flaw - it'll treat <code class="lang-js">} {</code> exactly the same as <code class="lang-js">{ }</code>. This is a pretty big problem, but I need to do <em>some</em> work today.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Writings by Shoofle</title>
<script src="/static/jquery.min.js" type="text/javascript"></script>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article>
<svg id="weeeeedblog" width="400px" viewBox="-100 -100 200 200" xmlns="http://www.w3.org/2000/svg" height="400px">
<g id="weeedblog">
<circle cx=0 cy=70 r=10 />
</g>
</svg>
<script type="text/javascript">
$(document).ready(function() {
var angle = 0;
setInterval(function() { $('#weeedblog').attr('transform', 'rotate(' + angle + ')'); angle += 3;}, 40);
});
</script>
</article>
</body>
</html>

104
finalized/project_list.html Normal file
View File

@ -0,0 +1,104 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Projects, by Shoofle</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="row-fluid">
<div class="span8">
<div class="row-fluid">
<div class="span3">
<div class="small_project web">
<p class="description"><a href="miscellany#auriga">HTML/CSS/JS Showcase on a Flask Server!</a></p>
<p class="name">Auriga! (<a href="http://li60-203.members.linode.com">you're lookin' at it!</a>)</p>
</div>
</div>
<div class="span3">
<div class="small_project writing">
<p class="description"><a href="miscellany#silver-asterism">Blog about Games as Art</a></p>
<p class="name">Some friend sand I occasionally write at <a href="http://silverasterism.blogspot.com/">Silver Asterism</a></p>
</div>
</div>
<div class="span6">
<div class="small_project game">
<p class="description"><a href="mindjail-engine">A 2D Physics-Based Game Engine in Python with OpenGL</a></p>
<p class="name">The Mindjail Engine, my hand-crafted 2D game engine!</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span4">
<div class="small_project art">
<p class="description"><a href="miscellany#spinning">Performance Art: Skilled Manipulation of Inertia Props</a></p>
<p class="name">I <a href="spinning">spin Poi and Staff</a> in my spare time</p>
</div>
</div>
<div class="span4">
<div class="small_project game">
<p class="description"><a href="shoof-shoof-revolution">DDR Clone in Javascript and SVG</a></p>
<p class="name">Shoof Shoof Revolution?</p>
</div>
</div>
<div class="span4">
<div class="small_project writing">
<p class="description"><a href="miscellany#lambdamoo">Worldbuilding is Fun, Or: Adventures in LambdaMOO</a></p>
<p class="name"><a href="city-on-the-river">City on the River</a>, and environs</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span6">
<div class="small_project web">
<p class="description"><a href="play-for-x">An Alternative to Rolling Dice in Tabletop RPGs</a></p>
<p class="name">Play for X</p>
</div>
</div>
<div class="span6">
<div class="small_project coding">
<p class="description"><a href="distributed-speakers">Free Ad-Hoc Sound Systems</a></p>
<p class="name">Project idea: Distributed Speakers</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<div class="small_project writing">
<p class="description"><a href="tamari">Ever heard of a Tamari Lattice?</a></p>
</div>
</div>
<div class="span6">
<div class="small_project writing">
<p class="description"><a href="language-for-games">If you were writing a language for game dev, what unorthodox features would you include?</a></p>
</div>
</div>
<div class="span3">
<div class="small_project writing">
<p class="description"><a href="oauth">I wrote about how I use oauth to make tumblr bots.</a></p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span4">
<div class="small_project art">
<p class="description"><a href="miscellany#guitar">I try to strum a guitar now and then. Spoiler: there are no recordings.</a></p>
</div>
</div>
</div>
</div>
<div class="span4">
<div class="about">
<h1>Hi! I'm Shoofle. I'm a lot of person.</h1>
<p>I graduated from the University of Virginia in 2012, majoring in math and physics with a minor in computer science. I'm really good at math. I'm pretty good at programming.</p>
<p>I've always loved to draw, but never focused enough time on it. I'm an intensely creative person and I like to think that this comes out in everything I do - and I'm trying harder every day.</p>
<p>I'm a passionate advocate for feminism, transgender rights, queer rights, and social justice in general.</p>
<p><span class="label label-important"><i class="icon-warning-sign"></i>Warning!</span> This website is still very much under construction. I'm in the process of figuring out how I want this website to look and function.</p>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 KiB

BIN
finalized/static/WHA.epub Executable file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 KiB

6
finalized/static/annyang.min.js vendored Normal file
View File

@ -0,0 +1,6 @@
//! annyang
//! version : 0.2.0
//! author : Tal Ater @TalAter
//! license : MIT
//! https://www.TalAter.com/annyang/
(function(){"use strict";var a=this,b=a.webkitSpeechRecognition||a.mozSpeechRecognition||a.msSpeechRecognition||a.oSpeechRecognition||a.SpeechRecognition;if(!b)return a.annyang=null,null;var c,d,e,f="en-US",g={start:[],error:[],end:[],result:[],resultMatch:[],resultNoMatch:[]},h=!1,i="font-weight: bold; color: #00f;",j=/\s*\((.*?)\)\s*/g,k=/(\(\?:[^)]+\))\?/g,l=/(\(\?)?:\w+/g,m=/\*\w+/g,n=/[\-{}\[\]+?.,\\\^$|#]/g,o=function(a){return a=a.replace(n,"\\$&").replace(j,"(?:$1)?").replace(l,function(a,b){return b?a:"([^\\s]+)"}).replace(m,"(.*?)").replace(k,"\\s*$1?\\s*"),new RegExp("^"+a+"$","i")},p=function(a){for(var b=0,c=a.length;c>b;b++)a[b].apply(this)};a.annyang={init:function(j){d&&d.abort&&d.abort(),d=new b,d.maxAlternatives=5,d.continuous=!0,d.lang=f,d.onstart=function(){p(g.start)},d.onerror=function(){p(g.error)},d.onend=function(){p(g.end),e&&a.annyang.start()},d.onresult=function(b){p(g.result);for(var d,e=b.results[b.resultIndex],f=0;f<e.length;f++){d=e[f].transcript.trim(),h&&a.console.log("Speech recognized: %c"+d,i);for(var j=0,k=c.length;k>j;j++){var l=c[j].command.exec(d);if(l){var m=l.slice(1);return h&&(a.console.log("command matched: %c"+c[j].originalPhrase,i),m.length&&a.console.log("with parameters",m)),c[j].callback.apply(this,m),p(g.resultMatch),!0}}}return p(g.resultNoMatch),!1},c=[],this.addCommands(j)},start:function(a){a=a||{},e="undefined"!=typeof a.autoRestart?!!a.autoRestart:!0,d.start()},abort:function(){e=!1,d.abort()},debug:function(a){h=arguments.length>0?!!a:!0},setLanguage:function(a){f=a,d&&d.abort&&(d.lang=a)},addCommands:function(b){var d,e;for(var f in b)if(b.hasOwnProperty(f)){if(d=a[b[f]]||b[f],"function"!=typeof d)continue;e=o(f),c.push({command:e,callback:d,originalPhrase:f})}h&&a.console.log("Commands successfully loaded: %c"+c.length,i)},addCallback:function(b,c){if(void 0!==g[b]){var d=a[c]||c;"function"==typeof d&&g[b].push(d)}}}}).call(this);

View File

@ -0,0 +1,48 @@
<p>Okay, so this is the automadom. The automadom is my robot partner!
I like robots, I like sex, I specifically think that anthropomorphizing our computers is an important step towards making truly intelligent computers.</p>
<p>There's a huge world of artificial intelligence out there. The field of AI spans the gamut from basic algorithm questions - how do we most intelligently solve a given problem? - to the much more open questions of how to make a computer that is "alive". I think the former is much more practical and applicable and useful, and the latter is philosophically fascinating.</p>
<p>Well, kinda fascinating.</p>
<p>I believe creating computers that are "alive" is closer than we think, because, in the end, "alive" is a pretty damn arbitrary quality. It might even be deeper in the eye of the beholder than beauty. Here's what I think:
Computers are much smarter than us at any number of tasks; intelligence is not the measure of life.
Humans are good at a wide spectrum of things, including interacting with other humans.
Humans are extremely capable at learning from literally everything we do - I'd go so far as to say that this is our sole advantage over computers.
Humans have agency. But that's thorny, because how do I know that you're making decisions? Does it make any difference to me if you would have acted differently back there? I think the answer is no, and so I posit that agency is /also/ in the eye of the beholder.</p>
<p>So what do I want? Ultimately, I want to push for computers and robots that have to be acknowledged as being just as alive as you or me. I don't think it'll happen in my lifetime, but it's a fascinating problem and I've got lots of free time since I don't have a job at the moment.</p>
<p>The first precept up there indicates that it's probably not important to simply get better, more complicated algorithms and hardware. People have been shoving money and time at this problem for years and years, and made... well, not great progress. I don't see any robots with agency, or treated as though they have agency.</p>
<p>The second implies, for one thing, that human interaction is an important quality of living robots. When we can interact with a robot and not know it, it'll be hard to treat it like it's a robot! But it's also important that it be able to do other things, because humans like complicated things.</p>
<p>The third precept is probably the most important, because expansion... well, it might well be the purpose of life altogether. But it's beyond my little poking projects for the moment. It should be remembered, though - someday, there will be a computer that expands itself on its own. I don't think we'll have the singularity, but it'll be hard to claim that that's not an impressive step.</p>
<p>The fourth precept is the one I most directly concern myself with at this juncture. What qualifies one as having agency? Why doesn't my car have agency? It moves itself, it occasionally does things at me unprompted (beeping a check engine light, for instance), and it certainly has more physical power than me.
I posit that the distinction is simply that I do not treat my car as though it were alive.
My dog has agency, and I think this is somewhat incontrovertible. I think the distinction is that when my dog does things, I do not treat them as things that I did through my dog. My dog is:
unpredictable
self-starting
Why not make robots that have these traits? It's reasonably easy. And veritably, we have done so! Roombas are constantly being anthropomorphized all over the world. What I suggest is merely to give our robots personality such that we anthropomorphize them automatically - that anthropomorphization is built into the design rather than a funny byproduct.</p>
<p>Which brings me to this project.</p>
<p>I am a pervert, it is well-known. I like sex! I like robots. It seems reasonable to combine the two! Someday I'm going to have the money to buy and build sex toys, but today is not that day. For now, I want to make a robot that wants to have sex with me.
This is a needy sex bot. The basic idea is that they get horny, and need me to have sex with them or they get fussy and unhappy. Implementation-wise, this is (for now) basically just a timer that counts down until it complains at me that I need to go "play with it". In the future, that's going to mean playing with a specific sex toy - one that I build that will hopefully have sensors and such in it so that it knows what I did with it when. For now, it's just going to be an email or something.</p>
<p>It's fundamental to this project, though, that I be doing this for the pleasure of the robot, rather than for myself! There's a lot of reasons that I want and enjoy this, which I'm not going to get into - but for the meantime, think of this like here: I am building a robot which knows how to be pleased, and then I am acting in order to please the desires of this robot.</p>
<p>I think it's a big mental step, that can take us from using our machines to respecting them.</p>
<p>One last thing: the question of morality. There are a lot of questions of morality here, and I want to address some:
1. Is it right to be sexually using a helpless being like this?
"Sexually" is a red herring here. I'm not using this entity, any more than I'm using any of my friends by doing things we all enjoy with them. But there's a thornier issue underneath:
2. Is it right to create a being that has specifically the desires you want?
And... I'm not sure. But we treat it as totally just (laudable, according to some people) to create beings who have human desires, because no one objects to childbirth. Morality is complicated, but until there are robots who desire things that we are intentionally not giving them, we're probably in the clear. But...
In order to answer these questions I think you implicitly need to answer some larger questions of /why/ it's bad to infringe on someone's agency - and I absolutely think that's awful. But I think it's awful because it harms us and them to do so, and there are lasting higher-order effects when we as a society decide that it's okay to remove someone's agency.
So maybe we need conscientious coding about these things. Maybe there's an additional reason to ensure our code doesn't throw errors - not just so it works, but so that we don't train ourselves to ignore failures and complaints. </p>
<p>I think these are drop-dead fascinating topics, but this project doesn't hinge on any of the answers. I just want to get myself in the habit of being able to consider a robot as my partner, and consider a robot as having agency.</p>
<p>Also, the sex stuff. I want that too.</p>

View File

@ -0,0 +1,291 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Game Log</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<style type="text/css">
.accolades { display: inline-block; }
.thoughts { font-size: smaller; font-style: italic; }
#games-log p { margin: 0; }
.difficulty, .achievement {
border-radius: 2px;
border: 1px solid rgba(0,0,0,0.1);
}
.max {
background-color: black;
color: white;
}
.hard {
background-color: rgb(255,70,70);
}
.normal {
background-color: rgb(200,200,200);
}
.easy {
background-color: rgb(70,255,70);
}
.bad {
background-color: yellow;
}
</style>
<article id="games-log">
<h1>Games I've Played</h1>
<section>
<h2>Mostly Shooters and Stuff</h2>
<ul>
<li>Mass Effect 3 Multiplayer</li>
<li>Halo: Combat Evolved
<div class="accolades">
<span class="difficulty max">Legendary solo</span>
</div>
<p class="thoughts">I really liked Halo: CE. It was a strong game, and I've had a soft spot for the Halo story - I don't think it's particularly deep or told with finesse, but it's <em>fun</em>!</p>
</li>
<li>Halo 2
<div class="accolades">
<span class="difficulty hard">Heroic co-op</span>
</div>
<p class="thoughts">I've been telling myself I'm going to beat Halo 2 on legendary by myself since the day I beat Halo: CE on legendary. I feel the overwhelming urge to be able to say I've beaten the whole trilogy on the hardest difficulty! On the other hand, it's really goddamn hard.</p>
</li>
<li>Halo 3
<div class="accolades">
<span class="difficulty max">Legendary co-op</span>
<span class="difficulty max">Legendary solo</span>
<span class="achievement max">All Skulls found</span>
</div>
</li>
<li>Halo 3: ODST
<div class="accolades">
<span class="difficulty hard">Heroic co-op</span>
</div>
</li>
<li>Halo: Reach
<div class="accolades">
<span class="difficulty hard">Heroic co-op</span>
</div>
</li>
<li>Gears of War
<div class="accolades">
<span class="difficulty max">Insane co-op</span>
</div>
</li>
<li>Gears of War 2
<div class="accolades">
<span class="difficulty hard">Hardcore co-op</span>
</div>
</li>
<li>Gears of War 3
<div class="accolades">
<span class="difficulty hard">Hardcore co-op</span>
</div>
</li>
<li>Half-Life</li>
<li>Half-Life: Blue Shift</li>
<li>Half-Life: Opposing Force</li>
<li>Half-Life 2 (and Episode 1) (and Episode 2)</li>
<li>Portal</li>
<li>Portal 2 (and co-op)</li>
<li>Army of Two</li>
<li>Army of Two: 40th Day</li>
<li>Operation Flashpoint: Dragon Rising
<div class="accolades">
<span class="achievement bad">Gave up after like 30 minutes</span>
</div>
<p class="thoughts">This game was terrible.</p>
</li>
<li>Resistance: Fall of Man
<div class="accolades">
<span class="difficulty hard">Hard? co-op</span>
</div>
</li>
<li>Resistance 3
<div class="accolades">
<span class="difficulty hard">Hard co-op</span>
</div>
<p class="thoughts">These games weren't very deep, but they had decent shooting and good weapons. Resistance 2 didn't have co-op, so I didn't play it - I just watched my friend do it.</p>
</li>
<li>Killzone 3
<div class="accolades">
<span class="difficulty hard">Veteran? co-op</span>
</div>
<p class="thoughts">This game offered alternate color modes for colorblindness, which is awesome. Shame the dialogue and plot was so literally laughable.
</p>
</li>
<li>Resident Evil 5
<div class="accolades">
<span class="difficulty hard">Veteran? co-op</span>
<span class="achievement max">Broke one level</span>
</div>
<p class="thoughts">My pal and I played this over one break, and we couldn't take it seriously. Once we beat it, we spent our hard-earned points on getting infinite ammo for a few weapons - and decided we'd try to unlock infinite ammo for every weapon. We didn't end up doing that, but we did get so practiced at going through one level - which drops the most money for the time you spend in it by <em>far</em> - that we beat the max difficulty time trial by <em>half</em>. The magnum in that game is ridiculously overpowered.</p>
</li>
<li>F.3.A.R.
<div class="accolades">
<span class="difficulty hard">Fearless? co-op</span>
</div>
<p class="thoughts">As far as I can tell, these people have no idea what the phrase "survival horror" even means.</p>
</ul>
</section>
<section>
<h2>Not Shooters</h2>
<ul>
<li>Pokemon: Red
<div class="accolades">
<span class="achievement normal">Beat the Elite Four</span>
</div>
</li>
<li>Pokemon: Silver</li>
<li>Pokemon: Diamond
<div class="accolades">
<span class="achievement normal">Beat the Elite Four</span>
</div>
</li>
<li>Prince of Persia: Sands of Time</li>
<li>Prince of Persia (2008)</li>
<li>Super Smash Brothers: Melee
<div class="accolades">
<span class="achievement hard">Every SP challenge</span>
</div>
</li>
<li>Super Smash Brothers: Brawl
<div class="accolades">
<span class="achievement max">Every achievement</span>
<span class="achievement bad">except one sticker</span>
</div>
</li>
<li>Mario Kart: Double Dash
<div class="accolades">
<span class="achievement max">Time Trial Staff Ghosts</span>
<span class="achievement max">Every Cup on Mirror</span>
</div>
</li>
<li>Mario Kart: Wii
<div class="accolades">
<span class="achievement max">Gold Medals on Everything</span>
</div>
</li>
<li>Okami
<div class="accolades">
<span class="achievement normal">All brush techniques</span>
</div>
</li>
<li>Legend of Zelda: Link's Awakening
<p class="thoughts">I think this might be my #1 pick for best Zelda game.</p>
</li>
<li>Legend of Zelda: Ocarina of Time</li>
<li>Legend of Zelda: Twilight Princess</li>
<li>Super Mario 64</li>
<li>Super Mario Sunshine</li>
<li>Final Fantasy</li>
<li>Final Fantasy 6
<div class="accolades">
<span class="achievement bad">Halfway</span>
</div>
</li>
<li>Final Fantasy 7
<div class="accolades">
<span class="achievement bad">Halfway</span>
</div>
</li>
<li>Final Fantasy 8
<div class="accolades">
<span class="achievement bad">Halfway</span>
</div>
<p class="thoughts">I feel like including Final Fantasy games that I haven't finished is still reasonable because not finishing a Final Fantasy game involves a similar time commitment as finishing many other games.</p>
</li>
<li>No More Heroes
<div class="accolades">
<span class="difficulty normal">Mild</span>
<span class="achievement normal">All Beam Katanas</span>
</div>
</li>
<li>No More Heroes 2: Desperate Struggle
<div class="accolades">
<span class="difficulty normal">Mild</span>
</div>
</li>
<li>Guitar Hero
<div class="accolades">
<span class="difficulty max">Expert</span>
</div>
</li>
<li>Rock Band
<div class="accolades">
<span class="difficulty max">Expert Guitar</span>
<span class="difficulty normal">Medium Drums</span>
</div>
</li>
<li>Super Metroid</li>
<li>Metroid: Fusion</li>
<li>Metroid: Zero Mission</li>
<li>Metroid Prime</li>
<li>Metroid Prime 3: Corruption</li>
</ul>
</section>
<section>
<h2>Extremely Memorable</h2>
<ul>
<li>Shadow of the Colossus
<div class="accolades">
<span class="difficulty max">Hard</span>
<span class="achievement hard">Time Trials (Normal)</span>
<span class="achievement max">Climbed the temple</span>
</div>
<p class="thoughts">I love this game so much I'm not even going to write about it. My thoughts would take up too much space.</p>
</li>
<li>Ico
<p class="thoughts">Ico was very pretty, and had some of the same quality that Shadow of the Colossus had, but it didn't tie together nearly so well.</p>
</li>
<li>Mirror's Edge
<div class="accolades">
<span class="difficulty normal">Easy</span>
<span class="achievement hard">Test of Faith (Pacifist)</span>
</div>
<p class="thoughts">This game has the best platforming I've had the pleasure of experiencing - and I'm a sucker for good platforming.</p>
</li>
<li>El Shaddai
<div class="accolades">
<span class="difficulty normal">Normal</span>
</div>
<p class="thoughts">I loved this game from an aesthetic point of view, but I kept waiting for the story to get interesting. I'm still waiting.</p>
</li>
<li>Zeno Clash
<p class="thoughts">The aesthetic really makes this game stand out.</p>
</li>
<li>Iji
<div class="accolades">
<span class="difficulty normal">Normal</span>
<span class="achievement normal">Somewhat Pacifist</span>
</div>
<p class="thoughts">Iji remains one of my favorite games of all time, for its excellent pseudo-morality system - one of the best I've seen.</p>
</li>
<li>Valkyrie Chronicles
<div class="accolades">
<span class="difficulty normal">Normal</span>
<span class="achievement normal">No deaths</span>
</div>
<p class="thoughts">Valkyrie Chronicles had such a cool gameplay and storytelling style that I have to include it.</p>
</li>
<li>No More Heroes
<div class="accolades">
<span class="difficulty normal">Mild</span>
<span class="achievement normal">All Beam Katanas</span>
</div>
<p class="thoughts">It's hard to describe this game. It was fascinating.</p>
</li>
<li>Elite Beat Agents
<div class="accolades">
<span class="difficulty hard">Sweatin'!</span>
<span class="difficulty max">Hard ROCK!</span>
</div>
<p class="thoughts">Probably my favorite rhythm game.</p>
</li>
</ul>
</section>
</article>
</body>
</html>

View File

@ -0,0 +1,150 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>A Language for Games</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>I like making games but sometimes I spend a lot of time doing stuff that is just annoying. What if a language or environment were designed to make those things easy?</p>
<hr>
<article class="project">
<header>
<h1>Snorlax Evaluation</h1>
<p>(define-by-reference, or: lazy evaluation taken to extremes)</p>
</header>
<p>Okay, so I've got a <code>Bomb</code> entity in my game. It's already got the behavior for flying toward its <code>destination</code>. What if I want to make a <code>SmartBomb</code> that tracks a target even if they move? I might do it something like this:</p>
<pre><code>SmartBomb.on_update = function (timestep) {
// other update behaviors go in this function as well
this.destination = target.position
}</code></pre>
<p>I have to explicitly tell my engine to execute this every frame. I think in terms of this kind of thing already, because I'm a math and physics person. Why can't I define relationships like this more simply? What if, in fact, this was the default way we referred to values? <span class="sidenote span4">It's worth noting that what I've thought of here is essentially just <a href="https://en.wikipedia.org/wiki/Functional_reactive_programming">functional reactive programming</a>. You can go read wikipedia if you want.</span></p>
<pre><code>SmartBomb.destination = target.position</code></pre>
<p>Any time something fetches the value of <code>SmartBomb.destination</code> it'll fetch the value of <code>target.position</code>. Writing it this way, instead of explicitly specifying which variables need to be kept up-to-date with other variables (and when they need to be updated!), means it's easier to give variables interesting dependencies on other values - like this <code>shield</code> which should float a certain distance between the <code>player</code> and the <code>enemy</code>:</p>
<pre><code>shield.position = player.position + shield.distance * unit_vector(enemy.position - player.position)</code></pre>
<p>If and when you <em>do</em> need to store something's value at a specific time, you can do that if you want - for example, this code when a bomb explodes:</p>
<pre><code>explosion_location = current_value(bomb.position)
// or maybe the syntax is like this:
explosion_location = bomb.position.current_value</code></pre>
<p>So far, everything's time-independent. For example, the <code>shield</code> object's <code>position</code> depends only on the positions of other objects.</p>
<p>Why not include time-dependence? A <code>bullet</code> has a <code>position</code> and a <code>velocity</code>, and these are related in a way that doesn't change at any point during gameplay. Why not codify that relationship in a similar way?</p>
<pre><code>bullet.position = bullet.position + time_step * bullet.velocity
// or maybe
bullet.position = bullet.position + bullet.position.time_since_last_calculation * bullet.velocity</code></pre>
<p>Okay, if this seems like impossible magic, I'm sorry. Let's walk through it:</p>
<ol>
<li>The global loop asks for <code>bullet.position</code> in order to know where it should be drawn.</li>
<li><code>bullet.position</code> looks at its cached value and sees that it's out of date.</li>
<li><code>bullet.position</code> subtracts timestamps to find the time step between now and whenever it last got calculated, and remembers this as <code>time_step</code></li>
<li>The time-dependence expression is now evaluated, using the last calculated value for <code>bullet.position</code>, and fetching <code>bullet.velocity</code> as normal.</li>
</ol>
<p>Hell, why not remember the entire history of a variable's values?<span class="sidenote span4">Performance.</span> When I fetch the last calculated value for <code>bullet.position</code>, why not go ahead and make that a graph-crawling reference-calculation just like everything else?<span class="sidenote span4">Reactive time-dependence is something to think about for an interesting mechanic, if nothing else - keeping track of dependencies on history means you could change a value at any point throughout the game's history and see how it changes things... Just like <a href="http://achrongame.com">Achron</a>! And the flaws that make it seem infeasible here wouldn't be a problem in, say, a turn-based time game.</span></p>
<p>Performance is a legitimate worry at that point. If you include the full-on time-dependence, then depending on implementation you might end up following a reference back through every frame the game's been through. This approach would obviously fall apart because every value in your world is going to be at least O(t) to calculate, which sucks - there should be <em>no</em> time-dependent complexity - but there are solutions:</p>
<ul>
<li>Careful design can ensure that your objects aren't too densely interlinked.</li>
<li>If you know you can do things more efficiently in some strenuous case, you can always hard-code it to extract current values and explicitly pass them around whenever you want.</li>
<li>Variables should carry metadata about when they last changed, and when their predecessors and descendents in the graph have changed - so that, in calculating the <code>shield</code>'s <code>position</code>, you don't re-fetch the <code>enemy.position</code>, if you know it hasn't changed.</li>
</ul>
<p>Besides which, we have fast computers, and without running into hard algorithmic complexity problems (collision detection, I'm lookin' at you!) it's not <em>that</em> easy to overload a modern gaming machine. And hey, maybe having declarative time-dependent values is a pipe dream - I don't care. The core idea is still awesome.</p>
<p>Here's an example (with more attention paid to data types) of how I'd come up with a vector that always points to a set offset from the position of <code>bullet</code>.</p>
<pre><code>bullet.position = Wrapper(v(1,2))
offset = v(3,4)
endpoint = bullet.position + offset
print endpoint</code>
<samp>--> WrapperSum(bullet.position, offset)</samp>
<code>print endpoint.current_value</code>
<samp>--> v(4,6)</samp>
<code>bullet.position.current_value = v(-10,0)
print endpoint.current_value</code>
<samp>--> v(-7,4)</samp></pre>
<p>Once I've started thinking and talking about this, I start wondering how much we need imperative game loops at all, for anything other than those troublesome time dependencies and feedback loops. I wonder...</p>
<pre><code>print bullet.contacts</code>
<samp>--> Wrapper(List&lt;Contacts&gt;)</samp>
<code>contact_acceleration = sum(contact.force_to_resolve for contact in bullet.contacts)
bullet.acceleration = contact_acceleration</code></pre>
<p>Assuming that force due to contacts is the only thing present in the world, then the bullet now feels enough acceleration to resolve all the conflicts. I mean, assuming contacts are simple to work with, and they always are. Right? Right?</p>
<p>I'm thinking about this a lot because this kind of referencing is very natural for things like defining interlinks between components in my game engine - I've got conflicting interests:</p>
<ul>
<li>I want to keep my vectors and properties lightweight, so that a <code>bullet</code>'s position should just be a vector, with which I should be able to do math as freely and easily as integers.</li>
<li>But I also want to ensure that the <code>collision component</code>'s position is the same as the <code>bullet</code>'s position! I'd like to do <code>collision_comp.position = bullet.position</code></li>
<li>And then I run into the problem that since I want my vectors as primitive as possible, if I change <code>bullet.position</code> then it's not going to change <code>collision_comp.position</code> because pass-by-value is the dominant paradigm.</li>
</ul>
<p>The solution I've been working with is wrap together <em>sets</em> of related data (as <i>components</i>). The <code>player</code> object has a <code>PositionComponent</code> and a <code>CollisionComponent</code>, and the <code>CollisionComponent</code> stores a reference to the <code>player</code>'s <code>PositionComponent</code> so that it can access it at any time. Actually holding those references constant means that there would be problems if the <code>player.position_component</code> suddenly changed to a different object - but that should never happen. I'm satisfied with this for the reasons I implemented it in the first place, but it doesn't get me the dynamic interlinking I'd like from something more like functional reactive programming.</p>
<p>So I don't know. I would love to have the <em>capacity</em> for functional reactive programming in my game engines, at the least! It's so useful for making bulletproof dynamic interactive systems.</p>
</article>
<hr>
<article class="project">
<header>
<h1>Time Integration</h1>
</header>
<p>I find myself writing cooldowns and countdowns all the time. Bullets can only fire so fast, bombs explode after five seconds, enemies spawn every 10-15 seconds. Every time I do it, I write the same boilerplate in the initialization:</p>
<pre><code>goomba.cooldown_max = 5 // 5 seconds to cool down
goomba.cooldown_timer = goomba.cooldown_max</code></pre>
<p>and the same boilerplate in the update function:</p>
<pre><code>if goomba.cooldown_timer &gt; 0:
goomba.cooldown_timer -= time_step
if goomba.cooldown_timer &lt; 0:
// insert end behavior here</code></pre>
<p>and somewhere, I activate this behavior (in response to a keypress, or proximit, or contact):</p>
<pre><code>goomba.cooldown_timer = goomba.cooldown_max</code></pre>
<p>I do this kind of thing so often that I feel like it should be one of the basic functionalities offered when I'm trying to describe game entity behavior.</p>
<pre><code>// initialization
goomba.shot_timer = new CooldownTimer(length=5s, on_end=function() {...})
// update functionality should be taken care of automatically, but if not:
goomba.shot_timer.step(time_step)
// activating the timer:
goomba.shot_timer.start()</code></pre>
<p>Now, wasn't that easy?</p>
<p>Of course, that's a pretty simple case! There are obvious extensions, though:</p>
<pre><code>goomba.blink_timer = new EndlessTimer()
goomba.blink_timer.interval = 5s
goomba.blink_timer.at_progress(0%, goomba.change_color) // change color at the start of each interval
goomba.blink_timer.at_progress(50%, goomba.take_step) // take a step once every interval halfway through</code></pre>
<p>And now making timed behavior for our goomba is easy! We can even speed up the goomba easily: <code>goomba.blink_timer.interval = 3s</code></p>
<p>Ideally, you don't need to even specify when to <code>step</code> your timers - that should be taken care of automatically. Of course, that means we'd want to <em>register</em> these timers instead of simply declaring them as a member - but maybe that's a matter of a grander architectural problem.</p>
<p>Although! If we <em>do</em> let the developer take care of stepping the timers, then we can also use it for things that <em>aren't</em> time-dependent:</p>
<pre><code>player.limit_bar = new Meter(length=100) // general verison of the CooldownTimer
player.on('hit by enemy', player.limit_bar.step)
player.limit_bar.at_progress(100%, player.limit_break)</code></pre>
<p>And now we've got a way for limit breaks to engage. Assuming there's a pre-existing event system and everything is architected carefully, it's now incredibly easy to fill meters in response to events.</p>
</article>
<article class="project">
<header>
<h1>Easy Peasy State Machinesy</h1>
</header>
<p>Why do I always have to actually use boolean flags (manually setting and unsetting them) to track the states of my entities?</p>
<pre>
<code>player.shielding = new Toggle()
keyboard.on('w pressed', player.shielding.next)</code></pre>
<p>I'm just throwing this syntax together off the top of my head. The syntax isn't important - what's important is that I should be able to describe this and have it happen.</p>
<pre>
<code>strobelight.color = new Sequence(['red', 'green', 'blue'])
strobelight.update = function(dt) {
strobelight.color.next()
strobelight.draw()
}</code></pre>
<p>Seriously, don't worry about this syntax - it's ugly and I just threw it together. The point is the kinds of things I should be able to do. This is all totally feasible.</p>
<pre>
<code>goomba.ai = new StateMachine(['sleep', 'suspicious', 'alert'])
goomba.ai['sleep'].transitions_to['suspicious'].on(goomba.near(player))
// the state 'sleep' should transition to 'suspicious' on the event goomba.near(player)
goomba.ai['sleep'].update = goomba.draw_snores
goomba.ai['suspicious'].transitions_to['alert'].on(goomba.sees(player))
goomba.ai['suspicious'].update = goomba.search_for_player
// shit we forgot to make a state for just patrolling
goomba.ai.add_state('patrol')
goomba.ai['patrol'].transitions_to['sleep'].after(5 seconds)
goomba.ai['patrol'].update = goomba.wander
// oh hey, she should occasionally wake up to patrol!
goomba.ai['sleep'].transitions_to['patrol'].after(5-10 seconds)</code></pre>
<p>So y'know. State machines are powerful. In order to really use them, you need a robust event system, or else they're a huge pain - it's still possible, you just... need to code it to check the conditions on each update loop. That could get expensive!</p>
<p>For the example of checking <code>goomba.near(player)</code> - which has an optional argument for the distance - that function checks if there's already a collision detection object attached to <code>goomba</code> for setting off proximity events. If there is, it returns the event handle, which can be used for binding behaviors to the firing of that event. If a proximity collider of the right size <em>doesn't</em> exist, then it creates one, and returns the event handle.</p>
<p></p>
</article>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Projects, by Shoofle</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article class="project">
<h1 class="title">Mindjail Engine!</h1>
<p>This is doubtlessly my most ambitious project. It's a little 2D game engine in python that I've been working on for a while now. It's got some basic physics (not accurate, but they feel right, and that's what counts) and it can zip along pretty respectably! I first started this project in my second year of college, but I soon abandoned it because (surprise of surprises!) I had a lot of classes to work on. When I finally picked it back up towards the start of 2013, I had grown a lot as a programmer and a human being, and there were changes to make. I still love this project, but the primary goal I had here was to never set a deadline for myself. The idea was that I should never say when this project will be done - because it's always in progress. I'm never fully committing myself to a flawed approach, and, although there's a bunch of ugly code here, I'm never afraid to go back and change how I did something.</p>
<h3>Tell me all about collision detection, it is my favorite subject!</h3>
<p>I was good at my data structures and algorithms class, and they're concepts I'm deeply familiar with. It's interesting, though, that nothing ever gave me such a good sense for how algorithmic complexities compare until I was doing real-time collision detection. It doesn't help that my chosen language was python - a decision that helped many things in this project, but not speed. <span class="sidenote">People will happily claim that python is too slow to ever make a real game with, but it's just not true. Computers are fast. I'm convinced that python is perfectly capable.</span> There've been a lot of data structures I used for this, and the source is somewhere - <span class="todo">I'm going to put those online at some point.</span></p>
<ul>
<li>When I started out, of course, I did the brute force N^2 test, because I just wanted to get objects moving around on the screen. This rapidly bottomed out once I got more than probably 30 objects on the screen. I don't remember, it was a long time ago! I knew from the start that I was going to have to do something better, so I did.</li>
<li>The next step was to roll something more advanced of my own. It had problems, but I'm still proud of how well it worked - I called it a sorted search list. The idea was that the list of objects in the world would be simultaneously kept sorted by each object's maximum and minimum extents on the x- or y-axes. When you're searching for a collision, then, you can cut down how many checks you do by searching upwards and downwards through the list until you find an object which is too far away to intersect - at which point no further objects could possibly be touching you. <span class="todo">I should make a picture.</span> Anyway, this system worked alright - if I remember correctly, I got up to around 50 objects on screen before it started slowing down a lot. I kept this around as a supporting data structure, but I didn't realize the massive flaws in it - in order to be certain about objects Foo and Bar not colliding, you need to run the check for Foo against Bar as well as Bar against Foo. Anyway.</li>
<li>
<p>After that I finally decided to ask the great oracle Google for help. Why didn't I use the knowledge of others before that? Well, they never really covered collision detection-specific data structures in my classes... I found out about a thing called a quadtree, and I was hooked. The idea is to recursively divide space into quadrants - objects northeast of center, northwest of center, and so on. This is the structure I spent the most time on, because I was dead-set on making it work. It was just so <em>cool!</em></p>
<p>Well. That was silly of me. There are a lot of problems. Suppose Foo is in the northwest corner, but Bar overlaps between northwest and northeast! There's no simple solution, and mine was to have a secondary data structure, which stored objects which couldn't fit into any of the corners.</p>
<p>At this point, I realized a structure-agnostic speedup I could use: Be mindful of whether objects move or not. If two objects never move, you never need to see if they collide. With this, the system got up to probably 80 objects on screen, with most of them stationary. Point being, it was a lot faster. I needed more, though! There was a huge problem:</p>
<p>The quadtree wasn't balancing. Balancing a regular tree data structure is one thing, but quadtrees have two dimensions to balance at once, which makes it infinitely more difficult... And I couldn't find a tutorial on it, nor figure it out on my own. All the tutorials either dealt with worlds that had a size limit for objects, or discrete-position finite-world grid systems. I didn't want to do either of those! So I started panicking.</p>
</li>
<li>The next solution was to try variations on quadtrees. I tried a bunch - changing what supporting data structure I used, switching from a strict quadtree - at each level dividing in quadrants - to a <a href="http://wikipedia.org/wiki/K-d_tree">k-D tree</a>: At each level, it divides the world in half, and switches the lines along which it divides the world each level. They're a little more flexible in a few ways, and in theory easier to balance (because you don't need to balance two dimensions at the same time so much). Unfortunately, there's a dearth of information about using them for collision detection, because everyone uses them for graphics and not much else. <span class="sidenote">At this point, I was running out of steam for recursive data structures, but I briefly considered extending my k-D tree to be the most general kind of <a href="https://en.wikipedia.org/wiki/Binary_space_partitioning">binary space partitioning tree</a>, wherein you don't just cycle your dividing axis through a set of options, but rather you divide along a completely new axis - not necessarily perpendicular! - at each level. This is used for raytracing a lot, but implementing it for these purposes would have been a pain. It would have just meant more balancing and trying to make the trees smart, which I didn't want to get too heavily into.</span> I gave up on this soon after, partly because...</li>
<li>I realized that my secondary data structure didn't work right. You needed to double-check every collision in order to be sure you had gotten all of them, and besides, sorting along one axis had little effect when you were frequently checking collisions which are grouped along the axis it doesn't sort on. I was basically back to square one. That's when I finally gave up on the tree - I would have needed to make it too smart in filling itself, I would have needed to balance it, I would have needed to have a good secondary data structure, and... It wasn't worth it. But then I found the light.</li>
<li>
<p>Spatial Hashing was something I'd heard of before, but mostly in the context of grid-based systems, finite worlds, and size-limited systems. I didn't want <em>any</em> of that. The typical spatial hashing scheme goes like this: You have a 2D array of lists. Each list corresponds to one grid space in your world, and so you only need to check against objects in adjacent squares. Here was my problem: Static arrays means you need to have a set size for your world, I don't like strict gridding, and I wanted to account for the fact that I might have large objects! But there's a solution, and, as it <em>always</em> seems to be in the world of data structures for practical everyday purposes, it's hash tables.</p>
<p>Here's how my system works: The Spatial Hash Grid is a dict. The keys are (x,y) tuples which index into the infinite space of grid squares. The items are a secondary collision-detection data structure, which contain the objects. To find the key for an object, you just have to integer divide its coordinates. Since it just uses python's built-in dicts, it's hashing the tuples - so you never need to pre-specify the size of your world! The thorniest thing to get around was storing large objects. I tried throwing items into all the grid squares they touch, but it was too slow. The solution I ended up using is to make multiple tiers.</p>
<p>When you put an object in the grid, it checks its size (by looking at the min/max x/y coordinates, which shape objects contain). If it's small enough to fit in this grid - that it will only ever occupy one square and its neighbors - then it goes in the dict we're looking at. Otherwise, the grid makes a new grid - with twice the grid size! Larger objects get shoved up the ladder, and when you're checking for collisions, you traverse up the ladder until you hit the end. This is reasonably fast so long as you don't have unreasonably many objects on different size scales.</p>
<p>And it works! I haven't done extensive testing, but it goes up to 150 - 40 moving around, and the rest stationary - with 60FPS. I've gotten some slowdown after that (only from adding more moving objects) but it's certainly fast enough for my purposes - and some of the speed loss is from unoptimized rendering - and it should be the case that a sparsely populated world will have nearly no slowdown from additional objects being chucked in.</p>
</li>
</ul>
<p>So I'm using Spatial Hashing, and there're ways for it to be sped up, but collision detection works pretty well for now - certainly well enough to focus on other things.</p>
<h3>Yes, but what about... anything else?</h3>
<p>Well, this project is about the engine, so I'm not prepared to go into the actual game I want to make with it, but there's certainly graphics and windowing to talk about. The graphics are, for the moment, all coded up with vertex lists that it generates when it runs, and they're far from optimized or anything. This is good because I learned a lot about OpenGL. This is bad because pretty much all of what I learned is now obsolete, since the computer I've been developing for doesn't... have support... for shaders. Whoops. I want to get more experience with shaders, but I don't have a computer that's good for it right now. Also, that will be more meaningful when I have a better model-&lt;engine pipeline for actually putting content into the engine.</p>
<p>Windowing is, for the moment, done through pyglet, which is a lightweight framework for windowing and opengl bindings. I like it okay, but it's not capable of doing much - I'd have to write my own GUI, which would be suboptimal (although not too hard) given that I could just get a framework to do it for me. What I'm trying to do now is switch to using PyQt, having tried wxPython briefly and found it very hard to work with. I'm still working through reimplementing secondary things that pyglet was doing for me, such as keyboard and mouse handling and clock/fps management. That's what the gui branch is for, on github.</p>
<h3>I guess links would be nice?</h3>
<p>Oh, yes! Right. <a href="http://github.com/shoofle/mindjailengine/">The repository</a> is up on github, and it should work for you if you fork it. You'll need to pip install pyglet, or fork their latest dev version if you're on a mac - there's some kind of unresolved problem with OSX or something. That's one of the reasons I want to switch to a different framework - better portability.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,43 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Shoofle and OAuth</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article class="project">
<h1>OAuth Adventures</h1>
<h4>(how does it work?)</h4>
<p>I recently had some exciting adventures involving trying to make a bot that posted to Tumblr. I eventually got it working (well, so far, just the posting part, but that's the one I didn't understand!)) but decided that I should write down what I learned about OAuth - and specifically, how to use it to write robots that post on their own.</p>
<p>The problem is that all the many guides to using OAuth are specifically geared towards the expected use case - I want my app to access users' data on other services like twitter! That's not what I want. I want my bot to be able to periodically run and access some tumblr accounts. This seems like it shouldn't be too hard, but I don't want to code in the password to this account! So what do I do?</p>
<p>So let's go through this backwards.</p>
<p>It's doable, very doable if you're using a library like oauth2, and extremely doable if you're using <a href="https://github.com/tumblr/pytumblr">tumblr's python api!</a> Either way, you're going to end up making requests to the <a href="http://www.tumblr.com/docs/en/api/v2">tumblr web api</a> using open authentication, and you're going to need a token to do it.</p>
<p>You need an Access Token.</p>
<p>An access token is keyed to your application, and it's been verified by Tumblr that a user has verified that you can use it. Think of it as the key to your user's content, which your user hands over to the application.</p>
<p>Of course, in your case, you're not going to care much about the exact mechanics of acquiring the access token, beyond the first time. You use the access token to sign your requests. How to do that is something I'm sure there are other tutorials for - if you're using oauth2, you simply need to specify it as the token for your client object:</p>
<pre><code>import oauth2 as oauth
client = oauth.Client(consumer, access_token)</pre></code>
<p><code>consumer</code> here refers to a key/secret pair, which you're given when you register your application. It lets tumblr know (through the oauth protocol) which application this is.</p>
<p>The <code>access_token</code> is <em>also</em> a key/secret pair, but you get it when the user logs in through oauth. The key and the secret are two strings of random-looking stuff, which is cryptographic magic.</p>
<p>If you're trying to figure out (as I was) how to get your python script to access user-restricted tumblr data, but you don't want to code the password to your account directly into the script, then the access token is exactly what you're looking for. I stuck it in a file that I don't share with anyone, from which my script imports the keys. Now I could share my script, but no one would have my keys - and even if they did, I could revoke its access to my account at any time.</p>
<p>If I'm using oauth2, then it works as above, and you use the <code>client</code> object to make your requests. If you're using tumblrpy, then you simply provide the consumer token's key and secret, as well as the access token's key and secret, to the library when you are initializing your client object. It's very easy!</p>
<h4>Okay but how do you do it?</h4>
<p>I can tell you're a smart cookie! That's a very good question. It's easy to use the access token, but how do you actually <em>acquire</em> it? As far as I can tell, the answer is to do the standard three-legged authentication process, but only do it once.</p>
<p>I just brought up the interactive python console and started typing in code as though I were trying to code a three-legged authentication workflow (this is the one you hear about when you google for oauth tutorials). Here are the steps of that process, in case you forgot:</p>
<ul>
<li>Acquire a consumer key and secret when you register your application.</li>
<li>Initialize an <code>oauth.Consumer</code> object with the consumer key and secret.</li>
<li>Initialize an <code>oauth.Client</code> object with your <code>oauth.Consumer</code> object.</li>
<li>Using that client, make a request to the request token url (for tumblr, it's <a href="http://tumblr.com/oauth/request_token">http://tumblr.com/oauth/request_token</a>). The response will contain a request token - a key and secret pair. This is used to acquire your access token!</li>
<li>Produce a URL to which to direct the user you want to get credentials for. This looks something like this: <code>http://tumblr.com/oauth/authorize?oauth_token=[your request token's key goes here]</code>.</li>
<li>When a user (such as yourself!) goes there, they will be prompted for what to do, and then when they grant authorization, they'll be redirected somewhere with a "verifier" in the URL arguments. If you were making a standard three-legged application, this would redirect you back to your website, which would then store and use the verifier. You just need to redirect yourself to somewhere and copy/paste the verifier out of your URL bar. The callback URL is set in the tumblr settings for the application.</li>
<li>Now you can use <code>token.set_verifier([some verifier])</code> to set your request token as being verified as good.</li>
<li>Your request token has been verified, so now you simply make a request (using that token, so <code>client = oauth.Client(consumer, request_token)</code>) to the access token URL - for example, <a href="http://tumblr.com/oauth/access_token">http://tumblr.com/oauth/access_token</a>. The response to <em>this</em> will finally contain... Drumroll!</li>
</ul>
<h4>The access token!</h4>
<p>So that's how it's done.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Projects, by Shoofle</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article class="project">
<h1 class="title">Play for X!</h1>
<p>This is a running project I'm working on. Check it out on <a href="http://li60-203.members.linode.com:7777/">another port on this very server</a>, with the caveat that it's not exactly the belle of the ball at the moment. Lately I've not been focusing on the styling and design so much as I've been focusing on...</p>
<p>The functionality! The super short version is this:</p>
<blockquote>
<p>Instead of rolling dice to see if you hit a monster, why not play a minigame instead?</p>
</blockquote>
<p>Tabletop roleplaying games (my experience is mostly with D&amp;D 3.5, but I've also been playing <a href="http://www.dungeon-world.com/">Dungeon World</a> and someday I want to run or be in a <a href="http://www.mimgames.com/window/">The Window</a> game) have the well-agreed on standard that you use dice to generate random numbers to check whether you succeed on chance-based tasks. This introduces instability into the game, which makes things tense and interesting and prevents the game from playing the same way every time!</p>
<p>But the only input the player actually has on how well they do, then, is the gameplay choices which apply various bonuses to their rolls. And that's kinda sucky! Because now that we all have computers, why not do something more interesting: </p>
<blockquote>
<p>When your character faces a skill-based task, you have to face one as well.</p>
</blockquote>
<h3>So what <em>is</em> Play for X anyway?</h3>
<p>It's a chatroom, plus a bunch of tiny javascript games. You and your friends all log into a room together, and you start playing your game over the voice chat system of your choice. When you (as the DM) decree that a player needs to make a skill check, you simply choose a minigame or microtask from the list, and drag it onto their name. It immediately pops up on their screen, and when they've completed it, their score is posted into the chatroom, and you can make a decision based on how well they did!</p>
<p>It's not finished yet. How the various parts of the system talk to each other has changed a bit as I've worked on this - the chat functionality works through websockets, and the games are slated to communicate their results to the client's browser by postMessage passing. This is partially implemented in...</p>
<p>The code! The source is available at <a href="http://github.com/shoofle/play-for-x/">the github project play-for-x</a>, and you're entirely welcome to inspect, criticize, suggest, fork, whatever, as you like. I'd be interested in hearing if you do something with it! Some parts are pretty messy, but the games themselves are fun to make.</p>
<h3>Can I check it out?</h3>
<p>Sure! If you want to just try the games, you should direct your eyeballs or eyeball-equivalents at <a href="http://li60-203.members.linode.com:7777/games/">this lil' website</a>, which lets you try out the minigames I've made, play with their configurations, and so on. It uses a <a href="http://jsoneditoronline.org">nifty json editor widget</a> that someone made.</p>
<hr>
<p>One of my favorite things about working on this project is that it really strips out all the cruft that usually comes with working on game projects - I don't need to worry about the engine, I don't need to worry about rendering engines, I don't need to make lots of textures. I explicitly set a goal of never writing a large game for this project. With that requirement in place, I'm totally free to experiment a <em>lot</em>, and just throw together little javascript games in a few hours. Indeed, most of the games you'll see here took me half an afternoon to write! Experimenting with different forms of gameplay, and exploring exactly that side of the craft, is one of my greatest interests.</p>
</article>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Projects, by Shoofle</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article class="project">
<h2 class="title">Shoof Shoof Revolution!</h2>
<p>As part of Play for X, I made a javascript-powered clone of DDR, using SVG. I like working with SVG and javascript - I can directly manipulate drawing rules and maintain an incredibly tight feedback loop as I develop. In this case, though, I was so proud of what I made that I kept trying to make it better. It's far from finished and I've set it aside as I work on other projects, but, in theory, this project was to make a simple javascript clone of DDR - but using the web audio API, I was going to make it so you could load in a file, entirely locally, and it would do automatic beat detection and generate an actual track for your song. Unfortunately, audio processing and beat detection is less than easy and simple when you're using javascript. <a href="http://github.com/shoofle/shoof-shoof-revolution">It's a cool project, and I'm interested in returning to it sometime.</a></p>
</article>
</body>
</html>

View File

@ -0,0 +1,60 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Projects, by Shoofle</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article class="project">
<h1>Spinning!</h1>
<p>I like to spin. If you youtube "fire spinning", you'll find out what I mean. I haven't yet actually spun fire, but I've been spinning my glowsticks and non-fire props for several years now, and I'm pretty dang good, if I say so myself! I started spinning at the University of Virginia (props to my Brown College spinners, and the short-lived poi club!) but now, living in Somerville, Massachusetts, I spin twice a week at the Medford Spinjam (at Tufts!) and the Boston Spinjam (at MIT). Check out their facebook pages to see when and where they meet, if you want to see me (and a bunch of others!) spinning our hearts out.</p>
<section class="prop" id="staff">
<h2 class="title">Poi!</h2>
<p>Poi was the first prop I learned. Once I managed to do a three-beat weave, I zoned out until my partner poked me that they were tired, and I realized that I was completely pooped, as I'd just spun for three hours without even thinking about it. That was in 2011, and I've been spinning poi off-and-on (and other props!) since then.</p>
<p>I like poi becasue it's relaxing. I can get into it and just spin for hours, and let the prop guide my movements. Gets boring, though!</p>
<p>Here's a list of the tricks I can do. If you don't spin a lot, feel free to ignore this. If you do, I apologize for the ones I have kooky names for.</p>
<ul class="tricks">
<li>In-plane spinning</li>
<li>Turns</li>
<li>Butterfly</li>
<li>Thread the Needle (forward only)</li>
<li>Pac-Man Twist</li>
<li>3-beat Weave</li>
<li>Weave extensions</li>
<li>Wrap-beat weave</li>
<li>5-beat Weave (forward only)</li>
<li>Reels</li>
<li>Chasing the Moon, Chasing the Sun, Moon Chasing Sun</li>
<li>Chasing where the Sun Don't Shine (same time only)</li>
<li>Windmills</li>
<li>Air Wraps</li>
<li>All manner of bicep wraps and leg wraps</li>
<li>Spiral wraps</li>
</ul>
<p>I spin a pair of flowlights with crystal cases. I've got the Sol coloration, and I love them dearly (although they have a tendency to turn off at the wrong times). I'm hoping to get a pair of Spectrum flowlights, or possibly build my own. I also have a pair of tailed poi I need to fix - they're so fun to spin!</p>
<p>Someday I'd like to build a pair of accelerometer-powered glowpoi - they could change color depending on the acceleration they're feeling. When you're spinning in a circle, that's directly dependent on speed - it could make for really nifty effects!</p>
</section>
<section class="prop" id="staff">
<h2 class="title">Staff!</h2>
<p>Since the end of 2012 I've also been very into spinning staff. I use a full-length staff that I made myself for super cheap - it's heavy as a car, but it's balanced alright. I spin a mix of traditional and contact staff, because what else would I do?</p>
<p>I like spinning staff because it's <em>fun</em>. Poi is relaxing, meditative; staff is, to me, much more energetic. I feel like I'm getting to play with this giant whirling toy, whereas with poi I feel like I'm guided by the inertia of the props.</p>
<p>Here's a (partial) list of tricks I can do:</p>
<ul class="tricks">
<li>Rotors</li>
<li>Weaves up to 5 beats (I think)</li>
<li>Half-Steves (right-to-left consistently, shakier on left-to-right)</li>
<li>Halos</li>
<li>Angel Rolls (only right-to-left)</li>
<li>Shoulder-Neck-Shoulder</li>
<li>Neck wraps, shoulder wraps</li>
<li>Fishtails (forward only, right hand only)</li>
<li>Rainbow Stalls</li>
<li>Conveyor Belts</li>
</ul>
<p>I'd like to learn to spin a dragon staff someday.</p>
</section>
</article>
</body>
</html>

View File

@ -0,0 +1,78 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Tamari Lattices</title>
<link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="/static/bootstrap/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="/static/shoofle.css" rel="stylesheet" type="text/css">
</head>
<body>
<article class="project">
<h1>Tamari Lattices!</h1>
<p>Tamari lattices are graphs (in the mathematical sense - a set of nodes with connections between them) describing a particular set of operations. I like them because they're pretty!</p>
<figure>
<img src="/static/tamari/tamari_3.png" alt="The (trivial) Tamari lattice for a three-element tree.">
<figcaption>A (trivial) Tamari lattice, generated by the associations of three elements. <a class="source" href="/static/tamari/tamari_3.dot">Source file.</a></figcaption>
</figure>
<p>Oh - oh dear. How'd that get there? Okay, that's not a very good example.</p>
<p>There are many ways to generate and think of a Tamari lattice. The way I prefer to think of it is this: Consider some binary operation - you take two elements to produce a new one. You want to combine several of these elements. So long as you have three or more elements, there are multiple ways to combine them.</p>
<p>The game we play is this: You're allowed to step from one way of combining the elements to another, but only by <i class="keyword">left-association</i>: turning <code>(a, (b, c))</code> into <code>((a, b), c)</code>.</p>
<p>The only remaining rule is that if you can step from one combination to another, the second one has to appear below the first one when you draw it.</p>
<figure>
<img src="/static/tamari/tamari_4.png" alt="A Tamari lattice for a four-element tree.">
<figcaption>As above, but generated by a four-element tree. <a class="source" href="/static/tamari/tamari_4.dot">Source file.</a></figcaption>
</figure>
<p>You can also think about it in terms of <a href="http://wikipedia.org/wiki/Tree_rotation">tree rotations</a> - but remember that that's a different tree than the one we're building as the Tamari lattice. Tree rotations are just another way to think of left-association and right-association.</p>
<p>If you look at <a href="http://wikipedia.org/wiki/Tamari_lattice">the wikipedia article on Tamari lattices</a>, you'll see a very pretty image:</p>
<img src="http://upload.wikimedia.org/wikipedia/commons/4/46/Tamari_lattice.svg">
<p>There are a lot of ways to reorganize a Tamari lattice, and it takes some artistic work to make one that really looks good. You can even visualize the same graph as <a href="http://en.wikipedia.org/wiki/Associahedron">a 3D shape called an "associahedron"</a>, but I like it in the simple gridded lattice form above. It reminds me how much beauty there can be in regularity - you can see the grid, but it doesn't look constrained by it. I might get a tattoo of the five-element Tamari lattice someday.</p>
<figure>
<img src="/static/tamari/tamari_5.png" alt="Tamari lattice for a five-element tree.">
<figcaption>Also the five-element lattice! Compare to the <a href="http://wikipedia.org/wiki/Tamari_lattice">example above, on wikipedia.</a>. <a class="source" href="/static/tamari/tamari_5.dot">Source file.</a></figcaption>
</figure>
<p>All these graphs with the ovals and the curvy lines were generated by yours truly! I made some python that would take a string representation of an association of a number of elements and convert it into an easily-manipulated memory representation. Then, a few tree rotations spit out all the possible results of left-associating on it, and it was relatively simple to print out a <code>.dot</code> file, parseable by <a href="http://www.graphviz.org/">graphviz</a>, that described the graph. It even labeled the nodes!</p>
<p><code>dot</code> happily converted them into the <code>png</code> files you're looking at. Of course, they don't have the human touch, so they're not organized into beautiful grid lines and 45° angles - but it can be fun to try to mentally reorganize them into a nicer shape. If you want, you can download the <code>.dot</code> source files for any of these, and play around with them in a graph-editing program (such as <a href="https://github.com/jrfonseca/xdot.py">XDot</a>)</p>
<figure>
<img src="/static/tamari/tamari_6.png" alt="Tamari lattice for a six-element tree.">
<figcaption>It's starting to get out of hand, I think.<a class="source" href="/static/tamari/tamari_6.dot">Source file.</a></figcaption>
</figure>
<p>Unfortunately, at a certain point I think it's going to get difficult to make these pretty. It turns out there are a lot of ways to associate a larger number of elements! Starting with a six-element tree or string, there are 42 elements (above). With seven, there are 132! Wowie!</p>
<figure>
<img src="/static/tamari/tamari_7.png" alt="Tamari lattice for a seven-element tree.">
<figcaption>Oh dear. <a class="source" href="/static/tamari/tamari_7.dot">Source file.</a></figcaption>
</figure>
<p>My server was chugging along trying to generate <code>tamari_8.dot</code> but I started getting messages from linode about going over my CPU quotas, so I canceled it - after it ran for an hour or so, without finishing. I think the seven-element one is messy-looking enough!</p>
<p>You can look at the <a class="source" href="/static/tamari/tamari.py">python script</a> I used to make this, but it's not particularly pretty - I was bored one day and decided I was going to figure out how to generate these... It's not exactly meant to be extensible. It's got a basic binary tree node class I threw together and a handful of (really ugly) helper functions. I just went through and rewrote it a bit to be nicer - the final output loop may please you if you enjoy <code>set</code>s and list comprehensions:</p>
<pre>
<code class="language-python">current_generation = set()
next_generation = {RootOfAllEvil}
edges = set()
labels = set()
while next_generation: # While there are trees to examine.
# Move to look at the next generation.
current_generation = next_generation
# Clear out the next gen to fill it with the children of this generation.
next_generation = set()
# Ensure there are labels for this generation.
labels = labels | set(label_from_node(parent) for parent in current_generation)
for parent in current_generation:
children = generate_children(parent)
labels = labels | set(label_from_node(child) for child in children)
edges = edges | set(str(parent) + " -> " + str(child) + ";" for child in children)
next_generation = next_generation | children
# Output a dot format stream!
args.output.write(u"digraph tamari {\n")
args.output.write(u"\n".join(labels) + "\n")
args.output.write(u"\n".join(edges))
args.output.write(u"\n}\n")</code></pre>
<p>The full script can be used by running <code class="language-bash">python tamari.py --length 4 | dot -Tpng > output.png</code> to produce a graph. <code>tamari.py</code> will print out to a specified file if you also include a filename: <code class="language-bash">python tamari.py --length 5 output.dot</code></p>
</article>
</body>
</html>

1109
finalized/static/bootstrap-responsive.css vendored Normal file

File diff suppressed because it is too large Load Diff

6167
finalized/static/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More