Feb 09 2011

How to implement OO programs on the standard NXT firmware

Category: Lego,Mindstorms,NXC,SoftwareSpiller @ 23:39

OOP is not at this time supported in NXC. Even though the memory management in the standard firmware might be limiting it is still possible to achieve a simple OOP implementation fairly easily. It will however be a bit inefficient memory wise and you will have the standard issues with tread-safety.

However actually testing my hypothesis in the large scale turned out to be cumbersome and thus I didn’t… If you notice anything that wouldn’t work out, something I forgot to consider or anything, please leave a comment : )

Object representation

The base for every object is a data structure containing all non-static variables. Lets consider a class like this:

class monster{
  public:
    static const unsigned char LEVEL_EASY = 0;
    static const unsigned char LEVEL_MEDIUM = 1;
    static const unsigned char LEVEL_HARD = 2;

  protected:
    unsigned char level;
    unsigned int hp;
    unsigned int max_hp;
    unsigned char attack[];

  public:
    monster();
    ~monster(){
      //The destructor could reset large arrays
      ArrayInit( attack, 0, 0 );
    }

    unsigned char difficulty() const{ return level; }
    float life() const{ return (float)hp/max_hp; }

    void receive_damage( unsigned char power, unsigned char attacker_level );
    void attack( monster opponent, unsigned char attack_id );
};

It would end up to something like this in NXC code:

const byte monster::LEVEL_EASY = 0;  //Yes, I know you can't do it like this in NXC...
const byte monster::LEVEL_MEDIUM = 1;
const byte monster::LEVEL_HARD = 2;
struct monster{
  byte level;
  unsigned int hp;
  unsigned int max_hp;
  byte attack[];
};
monster monster_paratemp;

The static variables are in global scope and the struct contains the other variables, nothing special here. Then an instantiation of the object is created, this will serve as a temporary object for function calls explained below.

Functions

At the NBC level, all variables are global and every reference to a variable is static and can’t be changed. Dynamic memory (other than arrays which content is dynamic) and pointers aren’t supported. This means that functions operate on a fixed set of variables and that all parameters must be passed by copying. If you want to pass a parameter by reference it is actually passed by copying and then copied back once the function returns.

This is problematic since in order to share the same function for multiple instances of the object we will need to tell the function which object to act upon, in short we need to pass the object by either reference or constant reference.

object.life(); //The way we want to call it in code
life( object ); //The way it would be implemented behind the scenes

NXC currently makes a copy for each function however this isn’t optimal if we have many functions in the object. Even if we have a lot of free memory and doesn’t care about wasting some, it will have a rather negative effect on the file size of the .rxe file due to the way variables are declared.

To avoid making this effect to large, I suggest sharing a single object for all functions. It could also optimize repeated function calls in some cases as you could leave the copy in the function copy instead of copying it back and forth. (It will cause longer delays if you need to protect it with a mutex though.) This is the reason for the monster_paratemp variable in the example.

(It would be nice to see this form for optimization for general functions, though it would be a lot more complicated since you need to take treading and sub-functions into consideration…)

If there is only one instance of the class, then the functions should of course operate on the object directly instead of using a copy. Inline functions should do the same. And if  the class only contains inline or static functions, a copy shouldn’t be needed at all.

Inheritance

Here is an example where the monster class is inherited:

class magic_monster: public monster{
  private:
    unsigned int sp;
    unsigned int max_sp;

  public:
    magic_monster(): monster(){
      ArrayBuild( attack, attack, 45, 30 );
    }
    ~magic_monster(){
      ArrayInit( attack, 0, 0 );
    }
    float magic_power() const{ return (float)sp/max_sp; }
    void attack( monster opponent, unsigned char attack_id );	//The attack might be magic
};

The NXC struct:

struct magic_monster{
  monster parent_monster;
  unsigned int sp;
  unsigned int max_sp;
};
magic_monster magic_monster_paratemp;

Instead of copying the monster struct and then add the extra variables, the struct is included as a sub-element. The reason is that this makes it a lot easier to call the functions defined in the inherited class. To call those, you simply copy parent_monster into monster_paratemp, call the function and then copy monster_paratemp back into parent_monster if necessary.

Multi-inheritance should also be easily possible this way, but I have never used it before so I don’t know if there are any special considerations to take…

Virtual functions

Unfortunately there is no way to properly implement virtual functions at runtime. It must be done completely at compile-time to work. (Well, fairly simple virtual functions could work, but the implementation would be inefficient and nowhere close to being sufficient.)

However because pointers aren’t supported, the cases where you need to save it in a parent class (and therefore having to rely on runtime detection) are rather few. There are only two cases I can think of. First one being function calls which accepts an object. And second one being the use of virtual functions directly in the base class, it would always use its own implementation (if available).

I think that we should depend on compile-time virtual functions, accept the changed functionality in the cases where it can’t be detected, but try to warn the programmer about issue. (Perhaps a custom keyword could be added to turn off the warning to show to compiler that you understand the issue?)

Functions accepting an object of the same class

One issue that appeared of using a single copy for the object parameter is apparent in the following example:

void monster::attack( monster opponent, unsigned char attack_id ){
  opponent.receive_damage( attack[attack_id], level );
}
monster dragon, ogre;
dragon.attack( ogre, 2 );

If we try to add how the temporary object is used together with the function calls it looks like this:

monster_paratemp = dragon;
attack( ogre, 2 );
  monster_paratemp = ogre;
  receive_damage( monster_paratemp.attack[2], monster_paratemp.level );
  ogre = monster_paratemp;
dragon = monster_paratemp;

Notice how dragon now is equal to ogre because it hijacked the temporary variable. Also note how the parameters send to the receive_damage function are actually holding ogre‘s values. (This might not always be the case though, depending on which order the parameters are copied.)

In order to make this work, we would need to copy the currently active object into another temporary object, move ogre into monster_paratemp like normal, call the function and then revert the whole process.

Hells begins once we start seeing stuff like this:

void monster::chained_attack( monster chained_monster, monster opponent, byte attack_id ){
  chained_monster.attack( opponent, attack_id );
}

Conclusion

It seems to me that OOP could very well work out just fine on the NXT using the standard firmware. It might not be exactly like C++, but considering the platform I would say it is quite satisfying.
In my opinion OO is a great way to program and I would love to see it in NXC. (I do realize that it will be quite some work to get it to parse C++ properly, but a man must dream. (Or make his own compiler))

I have only programmed OO for half a year though, so I’m sure  there are a lot of features that I don’t know and therefore haven’t considered how to (if possible) to implement.
And again, if you spot any errors, please leave a comment.


Jan 30 2011

NXC word-wrapping v0.5

Category: Lego,Mindstorms,NXC,Programs,SoftwareSpiller @ 23:25

Not really having done anything on my NXT-RPG game in a half year, I have now finally fixed some bugs in the word-wrapping code.

Word wrapping.nxc – version 0.5

Summery: Draws a text string on the NXT display, but will continue on the next line if the string is too long. This is done without breaking a word into two halves, it only breaks the string at spaces, preventing awkward looking text. Tabs are also handled correctly.
A custom made ricfont with variable width is used. The font is 8 pixels high (7px above the baseline, 1px beneath) and have a default width of 5 pixels.

Requirements: Enhanced firmware 1.28 (because of FontTextOut()) and the file “ASCII_7px.ric” present on the NXT.

Changes in this version

I had left a rather serious bug in the previous version which caused it to frequently break the string at wrong places.

The most significant change is the speed. I have optimized it in a few places which results the example string (in the screenshot) now only takes 76 msec to compile and render where it preciously took 109 msec. An improvement of about ~30% and the method to archive this was nothing special. I manually converted to if and one for statements at crucial places to NBC code instead of letting the NXC compiler do it. It is not an assembler optimization, I just do want the compiler should have done but fails to do properly (in terms of efficiency).
30% slower because of bad compilation… -__-

Update – v0.5.1

A few extra optimizations…

Speedtest: 62 ms

Downloads:

NXC source v0.5.1

Font file


Jan 22 2011

My comments on the LDraw file format spec

Category: LDraw,Lego,SoftwareSpiller @ 17:47

As I’m planning on writing a LDraw parser and viewer I read the LDraw.org File Format Version 1.0.0. However I find it rather vague so I’m sharing my comments on it.

Section LDraw Files:

“All LDraw files are plain text based. A specific character set encoding (UTF-8, ISO-LATIN1, US-ASCII7, etc) is not defined and software authors are free to use whatever encoding is appropriate for their target platform(s).”

I really don’t think it is a good idea not to specify any restrains on the character encoding. Just imagine a parser expecting ISO-LATIN1 getting a file encoding in UTF-16… But a file encoded in UTF-8 with BOM can be enough to cause parsing errors. My suggestion would be to enforce ASCII (8-bit) compatible character encodings and recommend UTF-8 (without BOM) if ASCII isn’t sufficient. What about newlines? For better “cross-platform portability” it should be defined which ways of defining newlines are allowed (in order to make sure parsers support them). Because if the parser doesn’t detect the newlines properly, it can’t read the file at all…

The whitespace characters allowed for keyword and parameter separation include spaces and tabs.

Include? Does this mean that other whitespace characters are allowed?

Section “Line Types“:

When describing the format (for example “0 !<META command> <additional parameters>”) spaces are used for separating keywords/parameters. I guess this means one or more whitespace characters?

Sub-section “Line Type 0“:

“0 // <comment>”, whitespace is required between “//” and the comment? (I guess I’m just not used to having whitespace there…) “<META command> is any string in all caps”, may there appear whitespace in the command? In an example in “Adding New META Commands” there is: “0 !LDPARSER EXAMPLE”, however I prefer that whitespace is not allowed as it makes separating the command from the parameters easier.

Sub-section “Line Type 1“:

For the file parameter there are several directories included, in which order should these be checked (in case of identical filenames)?

Section “META Commands“:

Many of the original commands are described by how they affect the original LDraw program and in my opinion this doesn’t belong in a spec for the file format. It should focus on the intended purpose instead of describing an implementation. The “LDraw.org Official Library Header Specification” seems to describe how to describe a file with meta-commands in order to include it in the official library, it doesn’t really specify the commands. Yet this document is the only documentation for several commands… On a side note, “0 Name: Filename.dat” and “0 Author: RealName [UserName]” are both not written in uppercase entirely and include a ‘:’ before the whitespace though this isn’t included in “List of Official META Commands”? According to the spec it can surely not be a META command since it isn’t in uppercase, but the LDraw team apparently treats it like that anyway?


Dec 20 2010

Small Technic walkers

Category: Lego,TechnicsSpiller @ 18:28

In Computer Science class we have a course about robotics and we where given the task to build a walking robot using Lego Mindstorms (with a 2 hour time frame).

Due to the limited amount of parts available we settled on a 4 legged design. However after 2 hours of struggle we went home empty-handed. Being annoyed that it didn’t work out, I continued at home to build a simpler Technic model. Two hours later I had this:

4-legged walker

[youtube=http://www.youtube.com/watch?v=GCmyogVerC0]
(Sorry for the bad lightning)

Balance showed to be crucial to the walking performance, the two round 2×2 bricks sole purpose is to balance the walker as without them it became (even more) unstable. It was probably also the issue with the failed NXT model as it had a lot more weight and fell over all the time. (The motor speed might have been too high too though.)
Interestingly enough, the surface had an effect on how the model should be balanced.

6-legged walker

I have previously build another small walker similar to the above but with 6 legs. It showed to be a much better solution than the 4-legged version as the stability is much better.

[youtube=http://www.youtube.com/watch?v=KpDoNH0Ibnc]

There is also a CAD file, I will upload it to brickshelf or something later…

Bonus

Made this for fun, a motor and battery box is inside making it able to drive. It is not that great though since the motor connection is pretty weak.


Nov 30 2010

Den hjælpende hånd

Category: Lego,MindstormsSpiller @ 23:21

At the high school I’m currently attending to, a full week was assigned to a special project with the theme: “Robots – the helpers of the future”. Everyone was divided into groups for four (except us as our group had 5 members) and at the end of the week were supposed to have created a robot that, err, could do something I guess.

Of some obscure reason we suddenly started building a robot that was supposed to be able to stir the contents of a pot to help your cooking or something. Well, we didn’t question it and just continued…

There was just one problem, the school never taught us how to build a working robot… After asking the teachers we were given a construction set called GEARZ and about 1-1½ hour later we had this:

It surely was hilarious, we even had to tape it onto the table to prevent it from falling (due to epic unbalance)… After the day ended I created something out of Lego instead.

However I decided NOT to use a NXT. After all, this was school and the main subject was Electronics. So I took the old RCX motors and sensors instead which are much easier to hook up with some circuit. For this project we used an Arduino board to control it.
Features:

  • Can stir or whip the contents in a pot
  • Can register the temperature and affect the speed of the stirring based on this
  • Can automatically turn on if you start a jet engine beside it. (Badly adjusted sound sensor…)
  • Can stop if you press a touch sensor twice

Photos:

There were two contents in this project, the teachers favorite was given a price (paid visit to a local restaurant) and the visitors favorite was given a price too (cinema tickets) as this whole thing was a giant PR stunt for the school….
Of some reason we actually managed to get on a shared first place for the visitors favorite.

Credits:

Ideas: The full group (5 members)
Construction: Me (took around 2×5 hours, I can’t believe I used that long for so little…)
Electronics design: the group members that receive electronics education (3 members including me) + some kind helping people from a university.
Programming: 50% by me, 50% by the 2 others (And no, the programming isn’t worth mentioning…)


Oct 17 2010

Motorized Off-Roader (#8297)

Category: Lego,MindstormsSpiller @ 15:35

This project was an attempt to combine the Lego Technic Off-Roader (#8297) and Lego Mindstorms NXT kit and thus motorizing the functions.

To summarize the project it ended up having these features:

  • NXT controlled steering
  • NXT controlled rear wheel drive
  • NXT controlled suspension height
  • PF motorized winch and lights (on-board PF power box)
  • 2 step manual transition
  • All original features (V8 engine, doors and such)
  • Almost no changes to the visual design

The electronics used:

  • NXT microcomputer
  • 3 NXT motors
  • PF power box
  • 1 small PF motor
  • 1 PF switch
  • 1 pair of PF lights

I ended up getting bored and stopped working on it so there is still some rough edges, but in general I’m pretty satisfied the result.

The following text is an old description of the project I found on my HDD:

Building

First step was to remove the designed parts carefully and making notice of where they were connected to the chassis in order to add it later when the NXT is added.

The original model has one motor connected to two functions by a manual gear switch. Since this wouldn’t work with a NXT, all the mechanics had to be changed.

Therefore, everything else than the fundamental chassis was ripped out.

Later on, some parts of the chassis was also changed to fit everything together.

Construction

The Power functions was preserved. The small motor was placed in the very front, directly connected to the winch. The battery box is also placed in the front together with the electrical switch.

In the back where the battery box had been, the NXT is placed with two NXT motors underneath. The last NXT motor is placed under the seats.

A manual 2 speed transmission was added in the bottom. The original differential was replaced with the older type differential because the new one isn’t strong enough and the gears would slip under the pressure.

Final product

There where almost no changes to the original design, so it looks very much like the original model other than the NXT parts which are visible.

I got bored in the end and finished the construction a bit too fast and didn’t fix the 2 step transmission. It sometimes under huge pressure jump out of gear. It should be easily fixed with a rubberband or something similar which can hold it in place.


Feb 18 2010

Rewrite of XML -> HTML conversion

Category: NXT RPG,WebdevelopmentSpiller @ 18:43

As I said previously, this process wasn’t very efficient. So I rewrote large parts of the code for the conversion of NXT RPG XML data files.

File system

All files have so far been placed in the same folder. So I decided to organize it a bit:

  • [folder] “global” : Contains all XML files that is used throughout the game
  • [folder] “graphics” : Contains all graphics used in the game
  • [folder] “levels” : Contains all levels used in the game
  • [folder] “shared” : Contains all XSLT files used for previewing/converting files
  • [folder] “strings” : Contains all strings used in the game
  • fileindex.xml : Contains a list over all game files

fileindex.xml

I wanted it to be possible to choose your own names, so “level42.xml” could instead be “Scary shopping mall.xml”. So fileindex.xml contains these names. It looks like this:
<files>
<global>
<gamestart>gamestart.xml</gamestart>
<gamedata>Gamedata.xml</gamedata>
<character_list>character list.xml</character_list>
<attack_list>attack list.xml</attack_list>
</global><levels>
<level id="1">lvl_1.xml</level>
<level id="2">lvl_2.xml</level>
<level id="3">lvl_3.xml</level>
</levels><graphics>
<sprite id="1">bg1.xml</sprite>
<sprite id="2">bg2.xml</sprite>
<characters>characters.xml</characters>
</graphics><strings>
<string id="1">strings_1.xml</string>
</strings>
</files>

It should be rather straight forward.

<global>

  • <gamestart> Filename of the file containing information needed for starting a new game.
  • <gamedata> Filename of the file containing information about the game, the name of the game and such.
  • <character_list> Filename of the file containing a list over all characters available in the game.
  • <attack_list> Filename of the file containing a list over all attacks available in the the game.

<levels>

  • <level id=”x”> Filename of the file containing the level data. “x” is a number from 1 to 254 which is used to identify the level.

<graphics>

  • <sprite id=”x”> Filename of the file containing information about the graphics used for level maps. “x” is a number from 1 to 254 which is used to identify the graphic sprites.
  • <characters> Filename of the file containing information about the graphics used for characters.

<strings>

  • <string id=”x” > Filename of the file containing a list of strings. “x” is a number from 1 to 254 which is used to identify the string list.

Issue in Firefox

This just doesn’t work that well in Firefox. Style sheets  accessed by “../” are blocked so basically nothing shows up if you try to open it. There is more about it here.

You can work abound the issue by opening “about:config” and change “security.fileuri.strict_origin_policy” to false. I don’t like it, but I’m not using Firefox so I’m not going to use time on it. However if any knows about a fix where it isn’t needed to move the files, inform me about it and I might fix it.

    Graphic files

    This needed to be a bit more organized. Up to now I had created a folder which was called the same as the ID for the level sprites (so something like “1”, “2” and so on…). Inside that folder was a image for each sprite, so quite a bit…

    In NXT RPG however the graphics are stored in one .RIC file. So I wanted to do the same here so the file looks like this:

    The sprites used for ID1

    Together with the image there is a XML file like this:

    <graphic>
    <filename>Lv1_bg.png</filename><type>bg</type>
    <id>1</id>

    <sprites>30</sprites>
    <size_x>10</size_x>
    </graphic>

    <graphic>

    • <filename> The filename of the image containing the graphic.
    • <type> The type of graphics. This can contain one of two values. “bg” is used for level sprites and “characters” is used for character sprites.
    • <id> The ID of “bg” graphics. If <type> is “characters” this value must be “0”.
    • <sprites> The amount of sprites contained in the image.
    • <size_x> The size of each sprite in pixels. All sprites are quadratic at this point.

    Implementation in HTML and CSS

    An empty <div> is given a class called “[type][id]_[number]” where “[type]” is <type> (“bg” or “characters”), [id] is <id> and “[number]” is the sprite you want. An example could be “bg1_4” if you want the sign sprite in the image above.

    The CSS is created dynamically and looks like this:

    [class*="bg1_"]{
    height:10px;
    width:10px;
    background-image:url('../graphics/Lv1_bg.png');
    }

    .bg1_4{
    background-position: -40px 0px;
    }
    A “.bg1_x” selector is created for each value of “x”

    Level files

    Those were main reason for rewriting it all. In Opera 10.50 beta it got corrupted if the HTML file became too large. Well, it really was large from the start, but most of it was just level previews that where repeated. So I wanted to remove those of course. (It didn’t work properly in IE either, but who cares…)

    I don’t fell like writing more right now, so I will make it short.

    Every level in fileindex.xml is created and assigned a (html) id based on the level (XML) ID. Then some CSS is assigned dynamically to connect the Events with the correct level preview. I will update this part later…

    Last heading

    There is still a few things to get done, but most of the functionality is already done.

    Any suggestions are welcomed.


    Feb 17 2010

    NXT RPG – an introduction

    Category: NXT RPGSpiller @ 16:32

    I thought the last post was pretty bad, and since most people wouldn’t know what NXT RPG even is, I thought I should say a little about it.

    NXT RPG

    Summery

    This is a Role Playing Game engine for the Lego Mindstorms NXT I’m developing in my spare time. The RPG type is highly inspired by the Pokemon games for Gameboy.

    Inspiration

    The first thought of making a RPG was back in the late spring/early summer of 2009. I hadn’t touched programming so much for some time and I simply wanted to start a new project. I have played the Pokemon games quite a bit and liked the gameplay, but there is also big issues with the actual gameplay. It is just a bit too easy, so I thought of making a RPG of something like Cat Mario.

    Then megamindstorms101 started the NXTasy Challenge #7, I thought why not as started work so I could enter it in the challenge.

    Why NXT?

    A lot of my friends have asked this, it is just some toy some kids and a few geeks have after all, so why?

    The NXTasy Challenge was just what finally got me started. I had recently moved to C++ instead of using Borland Pascal, but I only really knew how to make console programs in C++ I found quite limiting. With the NXT there is some easy commands to draw on the screen, so it would be easier to get started.

    But the most important reason is that the NXT “is just a toy”. It is not a highend computer with amazing 3D graphics, it is just a small minicomputer with 32KB RAM.

    Now you might ask, why is this a good thing? As weird as it might sound, I like limitations. Not because they are limiting, but that they wouldn’t necessarily allow you to use the first and easiest solution, you are forced to come up with other (and hopefully better) solutions. Every piece of code I write needs to be efficient because I don’t have a 3,6GHz CPU which wouldn’t even care. The NXT only have 32KB RAM, I can’t just waste too much memory. Secondly, there is only about 100KB memory back after the Firmware is installed, so program and game files must not become too big either. The 100x64px single color display is also fun.

    These limitations might mean that I can’t have all kinds of advanced features in the game. But they force me to select those which are actually important and not just bloat it with something most people wouldn’t even notice. And I need to implement those features as efficiently as possible.

    As I’m self-taught this is great practice to learn programming correctly and not just use whatever that seems to work.

    The game (or something)

    This didn’t exactly become a Cat Mario clone. I thought that should wait for a real game for PC if I still want to make another one after this project. Instead this became more like a Game engine, mostly because of my lack of inspiration. But also because I wanted to try to make something that will work for a multitude of games and not just one specific.

    The goal became that you could create a whole new game by just changing the data files without having to do anything with actual code.

    Gameplay

    You move in a strict quadratic grid, up down, left and right. Each step move to the next point in the grid, there are no middle positions. (There is some animation though.)

    Events happen only on steps. If you don’t move, nothing will happen.

    Battles are turn-based,  if you don’t make a move, nothing happens.

    Dependencies

    In order to make this game run properly you need:

    • nbc/nxc enhanced firmware 1.28
    • 4 touch sensors

    Tools used for developing

    • NXC
    • XML
    • XSLT
    • C++

    NXC is used for coding NXT RPG.

    The data files are stored in XML.

    XSLT is used to “preview” the  data files in a web-browser.

    An application written in C++ is used to convert the data files to binary data (since directly using the XML files would simply use too much space on the NXT). Right now XSLT is used as a middle step for converting the data files. (It creates a simple text code that is easier to convert.)


    Feb 13 2010

    Using :before/:after for extra background-images

    Category: NXT RPG,WebdevelopmentSpiller @ 00:32

    First a small introduction you might just as well skip… (actually, just scroll down to the bottom…=

    Level files used in NXT RPG are saved in binary of space reason, but this of course is hard to manually create and maintain and very much error prone. So I created my own tools using XML, XSLT, and made a small application to convert values to NXT binary values.

    It is pretty simple, the XML file stores data about the level, the XSLT file then creates a visual aid so it is easy to see what is wrong and a text code. The text code is then converted to binary by the application I wrote.

    End of story.

    The issue

    The XSLT file I made works, but it isn’t that efficient. The level is shown using X*Y images (which is often around 200); this really can’t be avoided. However I also include a small preview of the levels it is linked to, which adds another 200 per level. However I might need the preview in 2 places or more, but right now they aren’t reused. Because of this, the total amount of img elements normally adds up to 2000-4000. Ugh…

    Optimizing: Small steps first

    The level is viewed in two modes: normal and solid. Normal will show the level map as it looks like in the game, solid will show an overlay which tells which blocks on the map are, well, solid.

    This overlay is created by adding another X*Y images, where the overlay graphic are loaded on some elements. Not  that efficient…

    The solution became to use :before in CSS to add a absolutely positioned block inside the <img> which contain the overlay graphic.

    I tried it, it worked fine in Opera and it simplified the XSLT and resulting HTML a bit. Then I opened it in FireFox and :before didn’t work. Same thing in IE8. After a bit of testing I discovered that :before doesn’t work on <img> elements. After changing it to <div> and background-image the problem disappeared.

    I’m not really sure what is correct, but Opera seems to allow generated content everywhere, you can even say “head{display:block;content:”yay”}” and the “yay” appears at the top of the page. So I guess it is Opera that is at fault…

    Going crazy

    Then I wondered, how many “overlays” could I create with this method? By using both “content:url(‘img.png’)” and “background-image” you would have two. Then you could also add :after to double this to 4. Alternatively you could also create an underlay by setting background-image on the <div>.

    To test it out I tried to create a box with rounded corners using only a single <div>.

    It is basically the same as previously, however :before and :after is set to display:block. It is just useful for this example since it makes the positioning quite a bit easier…

    Box with rounded corners

    This will not work in IE6/7, so the advantage of using this instead of border-radius might not be that big… Secondly, positioning the imaged specified by “content:” can be a bit more tricky if the element is flexible in size… But it might be interesting in other cases…

    The code is here, without the images though:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en">
    <head>
    <title>Title</title>
    <link rel="stylesheet" type="text/css" href="main.css"/>
    <style>
    .Rbox{
    width:25%;
    background-color:#6267ff;
    padding:0 10px;
    }
    .Rbox:before, .Rbox:after{
    display:block;
    height:10px;
    background-repeat:no-repeat;
    background-position:top right;
    line-height:0px; /* remove issue with whitespace... */
    margin:0 -10px;
    }
    .Rbox:before{
    content:url('lt.png');
    background-image:url('rt.png');
    }
    .Rbox:after{
    content:url('lb.png');
    background-image:url('rb.png');
    }
    </style>
    </head><body>
    <div>something... texty texty... texty texty... texty texty... texty texty... texty texty... texty texty... texty texty... </div>
    </body>
    </html> 

    Tags:


    « Previous Page