满纸荒唐言:Processing 寻觅文字魅力篇

本文要说的:

  • 满纸荒唐言
  • 文字学习篇
  • 进阶成长录
  • 字体设计稿
  • 金陵十二钗

满纸荒唐言

乱哄哄,你方唱罢我登场,反认他乡是故乡;甚荒唐,到头来都是为他人作嫁衣裳。

“满纸荒唐言”出自小说《红楼梦》的开篇。正所谓,“满纸荒唐言,一把辛酸泪。都云作者痴,谁解其中味?”雪芹先生于悼红轩中,披阅十载,增删五次,纂成目录,分出章回,又题曰《金陵十二钗》,并题了该诗。

吾近来读之,甚喜。自以为文章合本意,故以此为引。

古人有云,“声不能传于异地,留于异时,于是乎文字生。文字者,所以为意与声之迹。”

当然,本文志在体味“脱帽露顶王公前,挥毫落纸如云烟”的洒脱与超然,而非细究文字的“传于异地,留于异时”的实用性。

就艺术的眼光看来,文字即无言之诗,无形之舞,无图之画,无声之乐。

这不禁让我想起了2008年北京奥运会开幕式的场景,国人向全世界展示中华文字艺术的魅力,好不壮观。

src="//player.bilibili.com/player.html?aid=16342941&cid=26662221&page=1" border="0" scrolling="no" allowfullscreen="true">

以下,是我用 Processing 制作的一个标题。代码在线,点击这里。

文字学习篇

于程序而言,其显示步骤如下:

step 1:Create the font;

至于创建字体,吾有三计——

计之一:利用创建字体工具手动创建字体。

在 Processing 工作窗口的“工具”(Tools)菜单中选择“创建字体…”(Create Font)命令,在弹出的窗口中选择字体和字号,点击“确定”(OK)完成创建。生成的 vlw 格式字体被默认放置于 data 文件夹中(若没有,PDE 会自动创建 data 文件夹)中。(“vlw”是 Processing 特有的字体格式)

计之二:使用用户系统中的字体。

如何查看用户系统中有字体呢?调用 PFont.list() 函数:

String[] list=PFont.list();
printArray(list);  // 在控制台输出字体名称

换而言之,我们可以在自己的电脑系统里安装好所需的字体,而后在 Processing 直接加载调用。

而美中不足的是,这种方式会导致程序的加载速度变缓,毕竟我们增大了加载难度。同时这也并非所有字体都可以使用,有些可能只适用于一个操作系统。

计之三:使用网上下载的字体。

此计可谓是结合了前二计的综合法。一为将下载好字体放置于 data 文件夹中(计一),一为在用户系统上安装好下载的字体(计二)。

step 2:Declare an object of type PFont;

声明变量:

// PFont + 变量名
   PFont font;	// 变量名为 font

step 3:Load the font;

这里理应有两种做法:

法之一:当需要将.vlw格式的字体加载到PFont对象时,我们调用 loadFont() 方法。

举个栗子:

PFont font;
// 字体必须位于“data”文件夹中才能成功加载
font = loadFont("LetterGothicStd-32.vlw");

法之二:利用 createFont() 方法,将“data”文件夹中的.ttf.otf文件或计算机上其他位置安装好的字体,动态地转换为 Processing 使用的格式。

举个栗子:

PFont myFont;

void setup() {
  size(200, 200);
  // 取消以下两行注释,以查看可用字体
  // String[] fontList = PFont.list();
  // printArray(fontList);
  myFont = createFont("Georgia", 32);
}

step 4:Specify the font;

此处应有 textFont()

textFont():设置将使用text()绘制的当前字体。其可输入一个或两个参数,字体变量和字体大小。若输入不包含字体大小,则字体将显示为字体的原始加载大小。值得注意的是,如果我们指定的字体大小与加载的字体大小不同,则可能会显示像素化或质量差的文本。

举个栗子:

textFont(f,36);

step 5:Specify a color;

上色工序,你可能需要:

fill(rgb)
fill(rgb, alpha)
fill(gray)
fill(gray, alpha)
fill(v1, v2, v3)
fill(v1, v2, v3, alpha)

step 6:Display text。

显示文本咯!详细请查阅text()

举个栗子:

text("word", 10, 90);

进阶成长录

厚地高天,堪叹古今情不尽;痴男怨女,可怜风月债难偿。

欲取文台幻境,且需步步为营。

在下文中,愚笨的笔者举了一个小小的栗子,以期读者能够领悟如何由浅入深、循序渐进地增长自身功力,成为绝世大侠。

小栗子的结构:

栗子形: 文字跳动
栗子壳: 天外飞仙
栗子肉: 落絮来书
栗子味: 还是得自己尝!
栗子皮:文字跳动

【明】袁宏道言:一林过雨芦花白,半壁疏云栗子黄。

代码在线,点击这里。
满纸荒唐言:Processing 寻觅文字魅力篇_第1张图片

栗子壳: 天外飞仙

无名氏言:毛刺随身雾里狂,针球栗子郁山乡。

代码在线,点击这里。
满纸荒唐言:Processing 寻觅文字魅力篇_第2张图片

栗子肉: 落絮来书

京津传言:堆盘栗子炒深黄,客到长谈索酒尝。寒火三更灯半灺,门前高喊“灌香糖”。

代码在线,点击这里。

栗子味: 还是得自己尝!

【元】王哲曰:栗子甘甜美芋头。

……

字体设计稿

关于创意字体设计,这里有两个可谓实用的库(NextText 已在 Processing 3 中弃用了,此处且不再提及):

Ⅰ:Geomerative,By Ricard Marxer

Introduction:

Geomerative 扩展了 2D 几何操作,以促进生成几何。其包括了 TrueType 字体和 SVG 解释器。

Ⅱ:Fontastic,By Andreas Koller

Introduction:

Fontastic 用于创建 TTF 和 WOFF 格式的字体文件。

所以,你该用谁呢? 以笔者的理解,前者如雕刻刀,后者若凿石斧。Geomerative 追求创意,Fontastic 旨在简易。

当然,我也收集到了一些使用 Geomerative 创建的字体:

满纸荒唐言:Processing 寻觅文字魅力篇_第3张图片
满纸荒唐言:Processing 寻觅文字魅力篇_第4张图片
满纸荒唐言:Processing 寻觅文字魅力篇_第5张图片
满纸荒唐言:Processing 寻觅文字魅力篇_第6张图片
满纸荒唐言:Processing 寻觅文字魅力篇_第7张图片
满纸荒唐言:Processing 寻觅文字魅力篇_第8张图片
满纸荒唐言:Processing 寻觅文字魅力篇_第9张图片
满纸荒唐言:Processing 寻觅文字魅力篇_第10张图片
满纸荒唐言:Processing 寻觅文字魅力篇_第11张图片

金陵十二钗

可叹的是,这里并不谈论《石头记》里面那些薄命女子,而是以87版《红楼梦》十二钗的美艳剧照为本,编写出一个以文字绘制肖像的创意小程序——“Twelve Beauties of Jinling”。

src="//player.bilibili.com/player.html?aid=35479486&cid=62200318&page=1" border="0" scrolling="no" allowfullscreen="true">

该程序我已在 OpenProcessing 上传,获取之,然而其运行效果尚不理想,但倘若读者利用 Processing 运行,它确实没有问题。这可能是网站的 Bug,目前我也没找到较好的解决办法。

因此,我在 CSDN 上补发了一份完整的源代码+资源,点击这里

以下,为其完整的源代码:

/**
 * Twelve_Beauties_of_Jinling
 *
 * By Hewes 18/11/6
 * https://blog.csdn.net/Hewes
 * Controls:
 * MousePress - 切换美人图
 *
 **/

//String[] imgNames = {"黛玉.jpg", "宝钗.jpg", "元春.jpg", 
//  "探春.jpg", "湘云.jpg", "妙玉.jpg", "熙凤.jpg", "惜春.jpg", 
//  "迎春.jpg", "巧姐.jpg", "李纨.jpg", "可卿.jpg"};
String[] imgNames = {"黛玉:欠泪的泪已尽.jpg", "宝钗:富贵的金银散尽.jpg", 
  "元春:欲知命短问前生.jpg", "探春:分离聚合皆前定.jpg", "湘云:为官的家业凋零.jpg", 
  "妙玉:无情的分明报应.jpg", "熙凤:痴迷的枉送了性命.jpg", "惜春:看破的遁入空门.jpg", 
  "迎春:欠命的命已还.jpg", "巧姐:有恩的死里逃生.jpg", "李纨:老来富贵也真侥幸.jpg", 
  "可卿:冤冤相报岂非轻.jpg"};
PImage img, bg;
int imgIndex = 0, index=0;
String[] lines;
PFont font1, font2;
char[] chars;
String displayText;
float cx, cy, r, degree;
float minWeight = 2, maxWeight = 6, currWeight;
float spacing = maxWeight;  // 字符间隔
float goldenRatio = (sqrt(5) + 1 ) / 2;
int iter = 0;

void setup() {
  size(900, 900);
  frameRate(30);
  font1 = createFont("楷体", 10);
  font2 = createFont("隶书", 80);
  bg=loadImage("bg.jpg");
  bg.resize(width, height);
  textAlign(CENTER, CENTER);
  lines=loadStrings("十二钗判词.txt");  // 读取并创建其各行的 String 数组
  nextImage();  // 下一张肖像
}

void draw() {
  pushMatrix();
  translate(width/2-120, height/2);
  if (cx>= 0 && 1.1*cx<= img.width && cy >= 0 && 1.1*cy<= img.height ) { 
    for (int i = 30; i > 0; --i) {
      index = index % displayText.length();  // 遍历文本
      degree = (iter * goldenRatio) * 360;  // 字符的角度
      r = sqrt(iter++) * spacing;
      // 计算字符的位置
      calcCharPos(img.width/2, img.height/2, r, (degree % 360));
      color pix = img.get((int)cx, (int)cy);
      currWeight = map(brightness(pix), 255, 0, minWeight, maxWeight);
      textSize(currWeight*2);
      fill(pix);
      text(chars[index], 1.3*(cx-img.width/2), 1.3*(cy-img.height/2));
      index++;
    }
  }
  popMatrix();
}

// 计算文字的位置
void calcCharPos(float x, float y, float radius, float angle) {
  cx = x + cos(radians(angle))*(radius/2);
  cy = y + sin(radians(angle))*(radius/2);
}

// 下一张肖像
void nextImage() {
  background(bg);
  textFont(font1);
  img = loadImage(imgNames[imgIndex]);
  img.resize(520, 520);
  img.loadPixels();  // 将当前显示窗口的快照加载到 pixels[] 数组中
  displayText=loadText(lines[imgIndex]);  // 文本更新加载
  // 显示美人名
  textFont(font2);
  fill(0, 80);
  text(chars[0], 750, 350);
  text(chars[1], 750, 550);
  textFont(font1);
  // 循环图片索引
  imgIndex += 1;
  if (imgIndex >=imgNames.length) {
    imgIndex = 0;
  }
  index = 0;
  iter = 0;
  cx = 0;
  cy = 0;
}

// 加载字符,需输入其索引
String loadText(String inText) {
  String allText=join(lines, " ");
  chars=new char[inText.length()];
  int i=0;
  int charPos = 0;
  if (imgIndex==0) {
    charPos=1;
  }
  char currChar;  // 当前字符
  // 遍历片段内字符,保存到 displayText 中
  while (charPos < inText.length()) {
    currChar = inText.charAt(charPos);
    charPos++;
    chars[i]=currChar;
    i++;
  }
  //println(new String(chars));
  return new String(chars);  // 返回将显示的判词
}

void mousePressed() {
  nextImage();  // 下一张肖像
}

欢迎关注
Hewes 的知乎专栏:https://zhuanlan.zhihu.com/c_123529691
Hewes 的 CSDN 博客:https://blog.csdn.net/Hewes

你可能感兴趣的:(Thinking,In,Processing)