Random feature

Pieter van Rosmalen's picture

Hello all,
I like to build a random feature in a typeface I'm working on.
Every character has four alternates and I like the fontsoftware to choose between them.
Can I do this in OpenType? And if so, how?
Thanks.
Pieter

brew's picture

Although rand is in the OT spec, no application currently support it (or are likely to any time soon).

You can emulate it in some way using calt though. Check our Thomas Phinney's sample code from the Adobe FDK forums: http://www.adobeforums.com/cgi-bin/webx?50@755.561rfSM0Gas.1@.3bbc5ea4

Pieter van Rosmalen's picture

Thanks you, but it's not working. I made a test font with code like this:

feature calt { # Connection or other contextual Forms
# Latin
lookup rotate {
sub @default @default' by @calt1;
sub @calt1 @default' by @calt2;
sub @calt2 @default' by @calt3;
sub @calt3 @default' by @calt4;
} rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
lookup rotate;
} calt;

@default = [a b c d e f g h i j];
@calt1 = [b c d e f g h i j a];
@calt2 = [c d e f g h i j a b];
@calt3 = [d e f g h i j a b c];
@calt4 = [e f g h i j a b c d];

I only get an a followed by a b. Is there something wrong in the code?
Thanks!

brew's picture

If I'm understanding your problem correctly, I think it's because each of your groups is effectively equivalent to the next. A better example would be

*****

@default = [a b c d];
@calt1 = [e g h i];
@calt2 = [j k l m];
@calt3 = [n o p q];

*****

where each of the groups contain unique characters.

so, aaaa becomes aejn.

Pieter van Rosmalen's picture

Yes, that's it. It's working. Thanks!
Is there a reason why 'lookup rotate' is repeated 13 times? It also works when it's only one time in the code.

brew's picture

To be honest, I'm stratching my head about that too. Seeing this thread reminded me to investigate that aspect of it further - if I find out, I'll post back here :)

Pieter van Rosmalen's picture

That will be great. Thanks for your help!

antiphrasis's picture

I've never done any OpenType programming, but I would guess that the 13 lookups work like modulo.

So instead of the eight alternatives coming in this order:
1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8

You get a pseudo-random order:
1 6 3 8 5 2 7 4 1 6 3 8 5 2 7 4

But there's probably more to it since I get the same series repeating...

.'s picture

You can make seemingly-random randomization, but not actually-random randomization. The OpenType version of LetTerror's Kosmik by John Butler does this, as does Christian Schwartz's Local Gothic, thanks to Tal Leming's brilliant code.

Nick Shinn's picture

Does the OT Kozmik still utilize just the three variants of each glyph, as in the original?

How many variants in Local Gothic?

twardoch's picture

Zapfino Extra LT Pro uses approx. 600 contextual substitutions to simulate pseudorandom typesetting. Each letter has between 4 and 7 variants. Some of the feature definition code was generated by Python code that I wrote.

The slides that I showed at TypoTechnica in London show some excerpts from the Python code and the OT code for Zapfino Extra LT Pro that I developed:
http://www.twardoch.com/adam/project.php?pid=0049

Regards,
Adam

paul d hunt's picture

yay! i found a cached version of Christian Robertson's thread for Dear Sarah on Google. Here it is with Christian's OT code hacks intact:

Excerpt of Christian's comments:

It's not true randomness. The OT code looks something like this:

@random_set_1 = [a d g j l m n o c];
@random_set_2 = [k e i n p q t v z];
@letter = [a b c d e f g h i j k l m n o p q r s t u v w x y z];
@letter_alt = [a.alt b.alt c.alt d.alt e.alt f.alt g.alt h.alt i.alt j.alt k.alt l.alt m.alt n.alt o.alt p.alt q.alt r.alt s.alt t.alt u.alt v.alt w.alt x.alt y.alt z.alt];

feature calt {
sub @random_set_1 @letter @letter' by @letter_alt;
sub @random_set_2 @letter @letter' by @letter_alt2;
} calt;

If you put in enough of these ugly hacks it starts to appear random. You would also want to make sure that no two letters were ever the same right next to each other. There are only so many of those that happen regularly in English, so I have added specific substitution rules for each of those cases (ee ff gg ll mm nn oo, etc.) Also notice that I have placed an @letter between the @random character. This makes the pattern even more difficult to spot. You might notice the funky h after every t, but you wouldn't notice the funky h after t-anycharacter, (unless you were freaking crazy).

In the case of this font, the fact that there are a different set of characters to connect to the o b v w also helps the font appear more random. The randomness feature above needs to be adjusted to get the connectors right for this one, which makes it more complicated. To be honest, I haven't completely figured it out.

Full Thread Here

also, for a bit of pseudo randomness, i think you could combine a couple contextual lookups that do different things. For example, you could have 2 sets of letters and have every third one switch to an alternate. Then have another lookup that changes double letter combinations so that the second letter in the combo gets switched out by its alternate. the interplay of these two functions will give a predictable, but somewhat random appearing effect. this is just one example, there are probably endless ways of combining effects such as this to get something that approaches randomness.

Pieter van Rosmalen's picture

Thanks for the information Paul, but I don't get this line:

sub @random_set_2 @letter @letter' by @letter_alt2;

You mean I have to make and @letter_alt2 class?

Pieter

paul d hunt's picture

hmmm, i'm not sure exactly what christian meant, but i think just using the @letter_alt class again would work...

deniserebryakov's picture

How should I change my OTF code to get the line “I need“ (image link http://clip2net.com/s/1qAvm)?

My code:

@letter = [b c d e f g h i j k l m n o p q r s t u v w x y z space];
@letter1 = [a c d e f g h i j k l m n o p q r s t u v w x y z space];
@letter2 = [a b d e f g h i j k l m n o p q r s t u v w x y z space];

feature calt {
lookup rotate {
sub a a' by a.calt1;
sub a.calt1 a' by a.calt2;
sub a.calt2 a' by a;
sub a @letter a' by a.calt1;
sub a.calt1 @letter a' by a.calt2;
sub a.calt2 @letter a' by a;
sub a @letter @letter a' by a.calt1;
sub a.calt1 @letter @letter a' by a.calt2;
sub a.calt2 @letter @letter a' by a;
sub a @letter @letter @letter a' by a.calt1;
sub a.calt1 @letter @letter @letter a' by a.calt2;
sub a.calt2 @letter @letter @letter a' by a;
sub a @letter @letter @letter @letter a' by a.calt1;
sub a.calt1 @letter @letter @letter @letter a' by a.calt2;
sub a.calt2 @letter @letter @letter @letter a' by a;
sub a @letter @letter @letter @letter @letter a' by a.calt1;
sub a.calt1 @letter @letter @letter @letter @letter a' by a.calt2;
sub a.calt2 @letter @letter @letter @letter @letter a' by a;
sub a @letter @letter @letter @letter @letter @letter a' by a.calt1;
sub a.calt1 @letter @letter @letter @letter @letter @letter a' by a.calt2;
sub a.calt2 @letter @letter @letter @letter @letter @letter a' by a;

sub b b' by b.calt1;
sub b.calt1 b' by b.calt2;
sub b.calt2 b' by b;
sub b @letter1 b' by b.calt1;
sub b.calt1 @letter1 b' by b.calt2;
sub b.calt2 @letter1 b' by b;
sub b @letter1 @letter1 b' by b.calt1;
sub b.calt1 @letter1 @letter1 b' by b.calt2;
sub b.calt2 @letter1 @letter1 b' by b;
sub b @letter1 @letter1 @letter1 b' by b.calt1;
sub b.calt1 @letter1 @letter1 @letter1 b' by b.calt2;
sub b.calt2 @letter1 @letter1 @letter1 b' by b;
sub b @letter1 @letter1 @letter1 @letter1 b' by b.calt1;
sub b.calt1 @letter1 @letter1 @letter1 @letter1 b' by b.calt2;
sub b.calt2 @letter1 @letter1 @letter1 @letter1 b' by b;
sub b @letter1 @letter1 @letter1 @letter1 @letter1 b' by b.calt1;
sub b.calt1 @letter1 @letter1 @letter1 @letter1 @letter1 b' by b.calt2;
sub b.calt2 @letter1 @letter1 @letter1 @letter1 @letter1 b' by b;
sub b @letter1 @letter1 @letter1 @letter1 @letter1 @letter1 b' by b.calt1;
sub b.calt1 @letter1 @letter1 @letter1 @letter1 @letter1 @letter1 b' by b.calt2;
sub b.calt2 @letter1 @letter1 @letter1 @letter1 @letter1 @letter1 b' by b;

sub c c' by c.calt1;
sub c.calt1 c' by c.calt2;
sub c.calt2 c' by c;
sub c @letter2 c' by c.calt1;
sub c.calt1 @letter2 c' by c.calt2;
sub c.calt2 @letter2 c' by c;
sub c @letter2 @letter2 c' by c.calt1;
sub c.calt1 @letter2 @letter2 c' by c.calt2;
sub c.calt2 @letter2 @letter2 c' by c;
sub c @letter2 @letter2 @letter c' by c.calt1;
sub c.calt1 @letter2 @letter2 @letter2 c' by c.calt2;
sub c.calt2 @letter2 @letter2 @letter2 c' by c;
sub c @letter2 @letter2 @letter2 @letter2 c' by c.calt1;
sub c.calt1 @letter2 @letter2 @letter2 @letter2 c' by c.calt2;
sub c.calt2 @letter2 @letter2 @letter2 @letter2 c' by c;
sub c @letter2 @letter2 @letter2 @letter2 @letter2 c' by c.calt1;
sub c.calt1 @letter2 @letter2 @letter2 @letter2 @letter2 c' by c.calt2;
sub c.calt2 @letter2 @letter2 @letter2 @letter2 @letter2 c' by c;
sub c @letter2 @letter2 @letter2 @letter2 @letter2 @letter2 c' by c.calt1;
sub c.calt1 @letter2 @letter2 @letter2 @letter2 @letter2 @letter2 c' by c.calt2;
sub c.calt2 @letter2 @letter2 @letter2 @letter2 @letter2 @letter2 c' by c;
} rotate;
} calt;

blank's picture

@default = [b c d e f g h i j k l m n o p q r s t u v w x y z space];
@alt1 = [a c d e f g h i j k l m n o p q r s t u v w x y z space];
@alt2 = [a b d e f g h i j k l m n o p q r s t u v w x y z space];

feature calt {
lookup rotate {
sub @default @default' by @alt1;
sub @alt1 @default' by @alt2;
sub @alt2 @alt2' by @default;
} rotate;
lookup rotate;
} calt;

deniserebryakov's picture

Thank You.

I understand that it's the general code.
But I don't understand how I should change it.
Could you explain me how to apply it to my case?

deniserebryakov's picture

Every character have two alternates.
This code doesn't show b.calt1 and c.calt2.
This code does not include the work of all the other letters.
How can I do it?

Your code:

@default = [b c d e f g h i j k l m n o p q r s t u v w x y z space];
@alt1 = [a.calt1 c.calt1 ....... space];
@alt2 = [a.calt2 b.calt2 ....... space];

feature calt {
lookup rotate {
sub @default @default' by @alt1;
sub @alt1 @default' by @alt2;
sub @alt2 @alt2' by @default;
} rotate;
lookup rotate;
} calt;

deniserebryakov's picture

I correct my code. All works.
Do not work correctly only a and j characters.

why?

My new code:

feature calt {
lookup rotate {

sub @default @default' by @calt1;
sub @calt1 @default' by @calt2;
sub @calt2 @default' by @default;

sub @default_jpqx @default_jpqx' by @calt1_jpqx;
sub @calt1_jpqx @default_jpqx' by @default_jpqx;

} rotate;
lookup rotate;
} calt;

@default = [a b c d e f g h i k l m n o r s t u v w y z space];
@calt1 = [a.calt1 b.calt1 c.calt1 d.calt1 e.calt1 f.calt1 g.calt1 h.calt1 i.calt1 k.calt1 l.calt1 m.calt1 n.calt1 o.calt1 r.calt1 s.calt1 t.calt1 u.calt1 v.calt1 w.calt1 y.calt1 z.calt1 space];
@calt2 = [a.calt2 b.calt2 c.calt2 d.calt2 e.calt2 f.calt2 g.calt2 h.calt2 i.calt2 k.calt2 l.calt2 m.calt2 n.calt2 o.calt2 r.calt2 s.calt2 t.calt2 u.calt2 v.calt2 w.calt2 y.calt2 z.calt2 space];

@default_jpqx = [j p q x space];
@calt1_jpqx = [j.calt1 p.calt1 q.calt1 x.calt1 space];

cerulean's picture

I don't fully understand this, but a and j are the first in each group. If there is no better answer, you can probably go around the problem by starting each group with another character that has no alternates. I assume each group ends with space for a similar reason.

Syndicate content Syndicate content