Gamasutra is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Gamasutra: The Art & Business of Making Gamesspacer
The Whimsy Of Domain-Specific Languages
arrowPress Releases
May 24, 2019
Games Press
View All     RSS








If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 

The Whimsy Of Domain-Specific Languages


September 3, 2008 Article Start Previous Page 2 of 3 Next
 

Whimsy Code

For my first iteration, I decided on "build" rather than "extend." I wanted the creation process to be interactive so I could edit the "code" in real time and immediately see results. I was also not sure about the level of abstraction I was going to use, and I wanted the language to be very loose, unconstrained by syntax requirements.

I also wanted to have more control over the speed of execution, so I decided to build a language parser using C++. I decided to call this language Whimsy and use .whimsy as a file extension to identify programs in that language (for example, lunar.whimsy).

The initial exploratory coding was fairly straightforward. The code would read in a file, split it into lines, split the lines into tokens, and then simply parse the lines with a series of "if" statements; a segment of the code is shown in Listing 1. (The complete code can be downloaded here.)

Listing 1: Ad Hoc DSL Parsing Code

if (token == string("rectangle"))
{
debug_log("NEW RECTANGLE");
CWhim *p_whim = new CRectangle();
Add(p_whim);
if (!grouping)
m_parse_context.clear();
m_parse_context.push_back(p_whim);
}
if (token == string("at"))
{
float x = (float)atof(tokenizer.NextToken().data());
float y = (float)atof(tokenizer.NextToken().data());
m_parse_context.back()->SetPosition(Vector2(x,y));
}

At its most basic description, Greenblat's painting is composed of colored shapes. There are concentric shapes within shapes, and some shapes have other shapes attached to them. There are shapes that split other shapes, and shapes that are lists of other shapes. It seemed to me that some form of hierarchical list of shape objects was needed.

I created an abstract base class of shape called a Whim (class CWhim), and from this I derived an abstract ConvexShape object and then derived other shapes, collections of shapes, and sub-shapes from these base classes. The "world" of a painting is just a std::vector container of these objects.

After the obvious primitives of rectangles and circles, I needed a way to create the ovals. I created a CWhim called CSuperEgg. A SuperEgg is a term for the solid version of a SuperEllipse, which is a shape defined by the equation (x/a)r +(y/b)r=1, where a and b are the length of the axes, and r defines the curvature. (See Wolfram in Resources for a full explanation.) I added a SuperEgg keyword to the parser and a little bit of code to read the parameters, and created the object.

Equations like the one above produce very nice squared ovals, but they are too precise to match the more freeform ovals in the paintings. To create a less precise shape, I created a new "distort" object, which simply takes a parent object and overrides the render function to add some periodic noise displacement from the center point. At a low frequency, it simulates the hand-drawn look of the original and can be applied to any of the ConvexShape primitives.

Next I added the petals. These simply took a parent ConvexShape object and positioned themselves at specified points along the perimeter. All ConvexShape objects (including the distorted shapes) have a member function that returns points at a given angular or linear distance around the perimeter, so the attachment points and base profiles of the petals can be calculated easily.

The height of the petals is specified as a multiple of the width of the base. I found that as much as possible it was best to keep all numbers relative to some other object or part of an object, as it makes dependent changes far easier.

Finally, I added an Inner shape, which simply takes one ConvexShape and creates a copy of it inside, shrunk by a certain ratio and optionally distorted. Using multiple Inner shapes made it very easy to reproduce the concentric multi-colored ovals.

These few primitives allowed me to reproduce, in part, a segment of "Lunar Module" (see Figure 3). The code used to generate the mock-up is shown in Listing 2.


Figure 3: Reproducing the detail using SuperEgg, Inner and Petal primitives of the Whimsy language.

Listing 2: Whimsy Code

superegg 0.15,0.10,3.5 at .3,.7 size 1.2 black distort .01
petals 14 0.05 size 1.8 petalblue
inner .88,.01 tvpurple
superegg .1,.2,2 at .20,.7 size .4 distort .01 tvlime
inner .65,.01 tvyellow
inner .45,.01 tvlightyellow
superegg .1,.2,2 at .3,.7 size .5 distort .01 tvblack
inner .85 tvbrown
inner .80 tvred
inner .75 distort .03 tvorange
inner .70 tvyellow
superegg .1,.2,2 at .4,.7 size .4 distort .05 tvblue
inner .6 distort .2 tvdarkblue
inner .4 petalblue


Article Start Previous Page 2 of 3 Next

Related Jobs

Ubisoft RedLynx
Ubisoft RedLynx — Helsinki, Finland
[05.24.19]

Senior/Lead Graphics Programmer
PixelPool
PixelPool — Portland, Oregon, United States
[05.23.19]

Backend Developer ?(Unreal Engine 4, Blueprint, C++)
Petroglyph Games
Petroglyph Games — Las Vegas, Nevada, United States
[05.23.19]

Senior Game Engineer (C++)
Sanzaru Games Inc.
Sanzaru Games Inc. — Foster City , California, United States
[05.23.19]

Junior Gameplay Engineer





Loading Comments

loader image