読者です 読者をやめる 読者になる 読者になる

OpenCV - iOS事始め

OpenCVのビルドから、OpenCVを使ったプロジェクトの作成まで。
ほぼ公式のチュートリアルの内容のままです。
http://docs.opencv.org/trunk/doc/tutorials/ios/table_of_content_ios/table_of_content_ios.html

OpenCVのビルドしてopencv2.frameworkを作る

適当なディレクトリでGitHubからcloneしてビルドします。ホームディレクトリにsrcというディレクトリを作って、そこでビルドしました。

% cd ~/src
% git clone https://github.com/Itseez/opencv.git

% cd /
% sudo ln -s /Applications/Xcode.app/Contents/Developer Developer
% cd ~/src
% python opencv/ios/build_framework.py ios

src/iosにopencv2.frameworkが作成されます。OpenCVを使ってアプリを作る際には、このフレームワークをリンクすることになります。

プロジェクトを作成する

簡単なサンプルということでSingle View Applicationを選択してプロジェクトを作成します。

プロジェクトを作成したらSupporting Files/{PROJECT_NAME}-Prefix.pchを以下の様に編集します。

#import <Availability.h>

#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif

// 追加
#ifdef _cplusplus
#import <opencv2/opencv.hpp>
#endif

#ifdef __OBJC__
  #import <UIKit/UIKit.h>
  #import <Foundation/Foundation.h>
#endif

次に、プロジェクトナビゲータのプロジェクトをクリックし、TARGETS->Build PhasesのLink Binary With Librariesのセクションにopencv2.frameworkを追加します。追加する際はAdd Other...から選択して追加します。


とりあえず、アプリをビルドできるか試すために、アラートを出すだけのアプリを作ることにします。


ViewController.m

- (void)viewDidLoad
{
  [super viewDidLoad];
  
  UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Hello"
                                                  message:@"Welcome to OpenCV"
                                                 delegate:self
                                        cancelButtonTitle:@"Continue"
                                        otherButtonTitles:nil];
  [alert show];
}

MacBook Air買った

Air二台目となるMacBook Air 13インチ 1.7GHz dual-core Intel Core i5
整備済品で安かったので買いました。

SSD128GBで入れるものを絞ったので、そのメモ。

開発系

ツール

  • MacFusion(SSH/FTPの接続先をマウントしてFinderからいじれる ※要MacFuse)
  • Dropbox
  • Evernote
  • Google 日本語入力
  • Homebrew

ユーティリティ系

  • TotalFinder
  • Quicksilver
  • MenuMeters (メニューバーにシステムの使用状況を表示する)

その他アプリケーション

  • InstantShot (スクリーンショット)
  • Chrome
  • Sparrow(メールクライアント)
  • Menubar Countdown (メニューバーにタイマーをつける)
  • Keynote
  • Prepo (psdのファイルをiOSアプリに使用するアイコンなどのサイズに一括で変換してくれる)

Core Graphics/Core Animation キソのキソ #1

Core Graphics / Core Animation入門

iOS/OS Xでのグラフィック・アニメーション関連のフレームワーク

UIKit

ビュー、ウィンドウ、ボタン、その他UI関連のコンポーネントを作るためのフレームワーク

Quartz 2D

iOSでグラフィックを描画する際に裏で働いてくれる奴。UIKitはQuartzを使っている。

Core Graphics

グラフィックのコンテキスト、画像の読み込み・描画などを手伝ってくれる奴

Core Animation

iOSでのアニメーション周りを手伝ってくれる奴

iOSでグラフィックを扱う際の基本

iPhoneでも世代によって解像度が違うので、pixelとpointの関係を考える必要がある。

iPhone 4iPhone 3GSの2倍の解像度を持つので、iPhone 3GSで320*480pxの四角形を描いてスクリーンを埋めようとしても、iPhone 4だとスクリーンの1/4のサイズの四角形になってしまうという違いが起こるので注意。

デバイスの解像度の違いに簡単に対応するため、pixelではなくpointを指定して描画するAPIを使う。

プロジェクトを作成する

以降のサンプルはSingle View Applicationテンプレートを使用する。 プロジェクトを作成したら、新規にUIViewを継承するクラスを作成。InterfaceBuilderでビューを選択し、Identity InspectorのCutom Classセクションのクラスを、作成したクラスに向ける。


UIViewを継承したクラスの.mには予め以下のコードが含まれる:

#import "MCGraphicsViewControllerView.h"

@implementation MCGraphicsViewControllerView

- (id)initWithFrame:(CGRect)frame
{
  self = [super initWithFrame:frame];
  if (self) {
    // Initialization code
  }
  return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
 // Drawing code
}
*/

@end

drawRect:メソッドがはビューが描画される時に自動で呼び出されるので、このメソッドにグラフィックを描画するプログラムを書いていく。

テキストを描画する

- (void)drawRect:(CGRect)rect
{
  // フォントをロードする
  UIFont *helveticaBold = [UIFont fontWithName:@"HelveticaNeue-Bold" size:40.0f];

  NSString *myText = @"yay";
  // フォントを指定して(40, 180)にテキストを描画
  [myText drawAtPoint:CGPointMake(40, 180)
             withFont:helveticaBold];
}
フォントを指定

フォントは「ファミリー > フェイス」の様に管理されているので、HelveticaのBoldが使いたければ「Helvetica(ファミリー) -> Helvetica Bold(フェイス)」の様に指定する。


システムに組み込まれているフォントは以下の様にして確認できる。

for (NSString *familyName in [UIFont familyNames]) {
  NSLog(@"Font Family = %@", familyName);
  for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName]) {
    NSLog(@"+-- %@", fontName);
  }
}


他のフォントを追加したい場合にはプロジェクトにドラッグドロップしてコピーする。


ターゲットへキーを追加する

フォント名を出力させると追加したフォントが表示されるので、後は同じ。

UIFont *AliceReglar = [UIFont fontWithName:@"Alice-Regular" size:40.0f];

NSString *myText = @"almost Alice.";
[myText drawAtPoint:CGPointMake(40, 180)
           withFont:AliceReglar];

色を指定

色の指定はUIColorオブジェクトを利用して指定することができる。

// ViewController.m
- (void)viewDidLoad
{
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  // ビューの背景色を白にする
  self.view.backgroundColor = [UIColor whiteColor];
}

[UIColor whiteColor]の様に色を指定したり

- (void)viewDidLoad
{
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  self.view.backgroundColor = [UIColor colorWithRed:1.9f
                                              green:1.0f
                                               blue:1.0f
                                              alpha:1.0f];
}

赤・緑・青・透明を0~1.0の範囲で指定して色を作ることもできる。

テキストに色を設定するにはコンテキストで色をセットする。

- (void)drawRect:(CGRect)rect
{
  // 暗めの青
  UIColor *myColor = [UIColor colorWithRed:0.1f green:0.3f blue:0.5f alpha:1.0f];
  UIFont *AliceReglar = [UIFont fontWithName:@"Alice-Regular" size:40.0f];

  // 色を指定する
  [myColor set];

  NSString *myText = @"almost Alice.";
  [myText drawAtPoint:CGPointMake(40, 180)
             withFont:AliceReglar];
}

@end

テキストを描画するにはdrawAtPoint:withFontでポイントを指定して描画する位置を決める他に、drawInRect:withFontで指定した矩形の範囲内にテキストを描画することもできる。

- (void)drawRect:(CGRect)rect
{
  // 暗めの青
  UIColor *myColor = [UIColor colorWithRed:0.1f green:0.3f blue:0.5f alpha:1.0f];
  UIFont *AliceReglar = [UIFont fontWithName:@"Alice-Regular" size:40.0f];

  // 色を指定する
  [myColor set];

  NSString *myText = @"almost Alice.";
  [myText drawInRect:CGRectMake(100,  // x
                                120,  // y
                                100,  // width
                                200)  // height
            withFont:AliceReglar];
}

Jam :: Require.jsを使う時に便利そうなパッケージマネージャ

Jam
クライアントサイドJSのパッケージマネージャ

インストール

npmから

% sudo npm install -g jamjs
...

% jam -v
0.1.11

パッケージをインストールしてみる

ダウンロードされるパッケージはカレントディレクトリに"jam"というディレクトリを作って配置されるので、プロジェクトのルートディレクトリを作る。

% mkdir jam-sandbox
% cd jam-sandbox/
% jam install jquery
installing from repositories jquery
Building version tree...
repositories checking "jquery"
installing jquery@1.7.2 (cached)
updating jam/jam.json
updating jam/require.config.js
updating jam/require.js
OK


% tree ./
./
└── jam
    ├── jam.json
    ├── jquery
    │&#160;&#160; ├── jquery.js
    │&#160;&#160; └── package.json
    ├── require.config.js
    └── require.js

2 directories, 5 files


一度に複数のライブラリをインストールしたい場合には

% jam install jquery underscore

みたいにする。

パッケージの検索は

% jam search domready
domReady 2.0.0-jam.1
    An AMD loader plugin for detecting DOM ready
OK: 1 total results (limit: 10)
% jam install domReady

インストールしたパッケージを使う。

require.jsを同時に用意してくれるので、require()でロードする

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>::Jam Sandbox::</title>
  <script src="jam/require.js"></script>
  <script>
  require(['domReady', 'jquery'], function(domReady, $) {
    domReady(function() {
      $('#out').text('Hello, world');
    });
  });
  </script>
</head>
<body>
  <div id="out"></div>
</body>
</html>

Jamで用意されたrequire.jsにはパッケージ情報が追加されているので、オリジナルのrequire.jsを使いたい場合には、別に"require.config.js"もロードする必要あり。


使わなくなったパッケージはドキュメントでは"jam remove {PACKAGE}" -> "jam clean"の手順で書いてあるが、"jam remove {PACKAGE}"のみで行けた。

% jam remove jquery
removing jquery
Building local version tree...
updating jam/jam.json
updating jam/require.config.js
updating jam/require.js
OK
// jam/jam.json
{
    "jam_version": "0.1.11",
    "dependencies": {
        "domReady": null
    }
}

コンパイルする

% jam compile vendor.js
compiling vendor.js
include domReady, jquery
OK: vendor.js
% ls
index.html jam        vendor.js

require.jsを含むパッケージを1つのファイルにしてくれる。

<script src="vendor.js"></script>


または、jam/require.jsをコンパイルすると、jam/require.jsに使うパッケージを含むようにコンパイルしてくれるので、HTMLを書き換える必要が無くなる。

% jam compile jam/require.js
compiling jam/require.js
include domReady, jquery
OK: jam/require.js


ただ、この後、"jam install hoge"すると、jam/require.jsは元に戻ってしまうので、別にビルド用のスクリプトを作るかする必要あり。


インストールしたパッケージのうち、特定のものだけコンパイルしたい場合には"-i"オプションを付ける。

% jam install jquery underscore backbone couchr
.
.
OK

% jam compile -i jquery -i underscore -o vendor.js
compiling vendor.js
include jquery, underscore
OK: vendor.js

サンプルアプリ

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>::Jam Sandbox::</title>
  <script src="jam/require.js"></script>
  <script>
  require(['js/app'], function(app) {
    app.initialize();
  });
  </script>
</head>
<body>
  <div id="out"></div>
</body>
</html>

js/app.js

define('js/app', [
  'jquery', 'js/utils'
], function( $, utils ) {
  var initialize = function() {
    utils.greeting();
    console.log('app ready');
  };

  return {
    initialize: initialize
  };
});

js/utils.js

define('js/utils', [], function() {
  var greeting = function() {
    console.log('initialize utility module.');
  };

  return {
    greeting: greeting
  }
});


パッケージをダウンロード

% jam install jquery
installing from repositories jquery
Building version tree...
repositories checking "jquery"
installing jquery@1.7.2 (cached)
updating jam/jam.json
updating jam/require.config.js
updating jam/require.js
OK


モジュールで依存するパッケージと定義しておくと、コンパイル時に解決してくれるので、

% jam compile -i js/app -o allinone.js

とすれば、js/app.jsが必要としているjqueryとjs/utilsをallinone.jsに含めてくれる

<script src="allinone.js"></script>

D3 メモ [SVG基礎編]

基本

SVGでグラフィックを描画する場合には、まずHTMLにsvg要素を追加して
描きたいシェイプを追加していく感じ。

function draw(json) {
  var w = 500,
      h = 500;

  var svg = d3.select('#container')
              .append('svg:svg')
              .attr('width', w)
              .attr('height', h);

  svg.append('svg:circle')
     .attr('cx', 50)
     .attr('cy', h/2)
     .attr('r', 30)
     .attr('fill', '#333');

  svg.append('svg:rect')
     .attr('x', 100)
     .attr('y', h/2 - 15)
     .attr('width', 50)
     .attr('height', 30)
     .attr('rx', 5)   // x方向の角丸
     .attr('ry', 5);  // y方向の角丸

  svg.append('svg:ellipse')
     .attr('cx', 190)
     .attr('cy', h/2)
     .attr('rx', 30)  // x方向の半径
     .attr('ry', 20); // y方向の半径

  svg.append('svg:line')
     .attr('x1', 10)
     .attr('y1', h/2 + 50)
     .attr('x2', 490)
     .attr('y2', h/2 + 50)
     .attr('stroke', '#333');

  svg.append('svg:polyline')
     .attr('points', function() {
       var points = [],
           baseY = 50;

       for(var x = 10; x <= 300; x+=10) {
         var point = '';
         if(x / 10 % 2) {
           point = x + ',' + baseY;
         } else {
           point = x + ',' + (baseY + 20);
         }
         points.push(point);
       }
       return points.join(' ');
     })
     .attr('fill', 'none')
     .attr('stroke', '#333');

  svg.append('svg:polygon')
     .attr('points', '10,100 10,130 50,150, 90,130 90,100')
     .attr('fill', 'none')
     .attr('stroke', '#333');

  svg.append('svg:path')
     .attr('d', 'M10,160 L10,190 L50,190')
     .attr('fill', 'none')
     .attr('stroke', '#333');
}


データをバインドして表示してみる。

function init() {
  d3.json('data/videos.json', draw);
}

function initData(json) {
  return _.map(json.data.items, function(item) {
    return {
      title: item.title,
      fav: item.favoriteCount,
      view: item.viewCount,
      uploaded: item.uploaded
    };
  });
}

function draw(json) {
  var data = initData(json),
      canvW = 800,
      canvH = 495,
      ch = canvH/data.length,
      max, w,
      svg;

  max = d3.max(data, function(d) { return d.view; });
  w = d3.scale.linear().domain([0, max]).range([10, ch]);

  svg = d3.select('#container')
          .append('svg:svg')
          .attr('width', canvW)
          .attr('height', canvH);

  // view数で半径を決める
  svg.selectAll('circle')
     .data(data)
     .enter()
     .append('svg:circle')
     .attr('cx', '50px')
     .attr('cy', function(d, idx) {
       return idx * ch + w(max) / 2;
     })
     .attr('r', function(d) {
       return w(d.view) / 2;
     })
     .attr('fill', '#333')
     .attr('stroke', '#3b93ff')
     .attr('stroke-width', function(d) {
       return w(d.view) / 4;
     });

  // titleテキスト
  svg.selectAll('text')
     .data(data)
     .enter()
     .append('svg:text')
     .text(function(d) {
       return d.title;
     })
     .attr('x', function(d) {
        return 50 + ch;
     })
     .attr('y', function(d, idx) {
        return idx * ch + w(max) / 2 + 5;
     })
     .attr('font-size', '12px');
}

D3 メモ[API]

API Reference
https://github.com/mbostock/d3/wiki/API-Reference

Event

<div id="container">
  <button class="clickme">click me</button>
  <button class="clickme">click me</button>
  <button class="clickme">click me</button>
  <button class="clickme">click me</button>
</div>
  var data = [
    { name: 'button1' },
    { name: 'button2' },
    { name: 'button3' },
    { name: 'button4' }
  ];

  d3.selectAll('.clickme')
    .data(data)
    .on('click', function(d, idx) {
      var posX = d3.event.pageX + 'px';
      d3.select('#out')
        .style('left', posX)
        .text(function() { return d.name; });
    }, false);


選択した要素にはonでイベントハンドラを登録できる。
イベントオブジェクトはグローバルなd3.eventを参照することで、対象の要素のイベントを取得できる。


イベントの発生時のマウス位置を、特定の要素内の相対位置で取得したい場合には
d3.mouse(document.getElementById('container'))のようにすると[x, y]の配列で取得できる。d3.touch()もある。

  d3.selectAll('.clickme')
    .data(data)
    .on('click', function(d, idx) {
      var pos = d3.mouse(document.getElementById('container'));

      d3.select('#out')
        .text(function() {
          return d + ' at (' + pos[0] + ',' + pos[1] + ')';
        });
    }, false);

データ取得

  • d3.xhr(url, callback)
  • d3.text(url, callback)
  • d3.json(url, callback)
  • d3.xml(url, callback)
  • d3.html(url, callback)
  • d3.csv(url, callback)

サンプルなので、予め取得しておいたデータをjsonファイルとしてローカルに置いておく
http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc&max-results=20&category=Music&format=5&orderby=rating&q=sonicyouth

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Demo</title>
  <!--[if lt IE 9]>
  <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->
  <style>
  .item { margin: 10px 0; }
  h5 { margin: 0; }
  ul {
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .info {
    font-size: 85%;
  }
  </style>
  <script src="js/libs/d3/d3.v2.js" type="text/javascript"></script>
  <script src="js/libs/underscore-min.js" type="text/javascript"></script>
  <script src="js/app.js" type="text/javascript"></script>
</head>
<body onload="init();">
  <div id="container">
    <ul></ul>
  </div>
</body>
</html>
function init() {
  d3.json('data/videos.json', draw);
}

function draw(json) {
  d3.select('#container ul')
    .selectAll('li')
    .data(json.data.items)
    .enter()
    .append('li')
    .attr('class', 'item')
    .html(function(d, idx) {
      return buildHTML(d);
    });
}

function buildHTML(d) {
  var tmpl = _.template(
    '<h5><%- title %></h5>' +
    '<img src="<%- thumbnail %>" />' +
    '<div class="info">' +
    '<span class="like"><%- like %></span> | ' +
    '<span class="view"><%- view %></span>' +
    '</div>'
  );

  return tmpl({
    title: d.title,
    thumbnail: d.thumbnail.sqDefault,
    like: d.likeCount,
    view: d.viewCount,
  });
}

配列操作

d3で扱うデータは配列が基本なので、配列操作のメソッドが色々用意されている。

function init() {
  d3.json('data/videos.json', draw);
}

// 欲しいデータだけ取り出す
function initData(json) {
  return _.map(json.data.items, function(item) {
    return {
      title: item.title,
      fav: item.favoriteCount,
      view: item.viewCount,
      thumbnail: item.thumbnail.sqDefault
    };
  });
}

function draw(json) {
  var data = initData(json),
      fMax, fMin, split;

  // fav数の最小・最大値
  fMax = d3.max(data, function(d) { return d.fav; });
  fMin = d3.min(data, function(d) { return d.fav; });

  // min/maxは一度に取得できる [min, max]
  fExtent = d3.extent(data, function(d) { return d.fav; });

  // fav数の昇順ソート
  favAsc = data.sort(function(a, b) {
    return d3.ascending(a.fav, b.fav);
  });

  // view数が10,0000以上のアイテムだけ取り出して降順にソート
  split = d3.merge(d3.split(data, function(d) {
              return d.view < 100000;
            }))
            .sort(function(a, b) {
              return d3.descending(a.view, b.view);
            });

}

d3.scale

fav数に対応した大きさの図形を描きたい時など
実際のデータの値のまま使うと

function init() {
  d3.json('data/videos.json', draw);
}

function initData(json) {
  return _.map(json.data.items, function(item) {
    return {
      title: item.title,
      fav: item.favoriteCount
    };
  });
}

function draw(json) {
  var data = initData(json);

  d3.select('#container')
    .selectAll('div')
    .data(data)
    .enter()
    .append('div')
    .attr('class', 'bar')
    .style('background', '#0088ff')
    .style('font-size', '12px')
    .style('height', '16px')
    .style('width', function(d) {
      return d.fav + 'px';
    })
    .text(function(d) {
      return d.title;
    });

}

fav数によっては、windowサイズを大きくはみ出してしまうので
例えば、最大でもwindow幅以内にして全データをスケールしたい場合にはScales APIを使う。

d3.scale.linear()

デフォルトで変域・範囲が0~1のスケールを作るので、必要な変域と、範囲を指定する

max = d3.max(data, function(d) { return d.fav; });

// 「0~fav数の最大値」を「0~windowサイズ」に収める
w = d3.scale.linear()
                    .domain([0, max])
                    .range([0, document.body.clientWidth]);

d3.select('#container')
  .selectAll('div')
  .data(data)
  .enter()
  .append('div')
  .attr('class', 'bar')
  .style('background-color', '#0088ff')
  .style('font-size', '12px')
  .style('height', '16px')
  .style('width', function(d) {
    return w(d.fav) + 'px';
  })
  .text(function(d) {
    return d.title + ':' + d.fav + '(' + w(d.fav) +  'px)';
  });

グラフの軸を描くときにも使える。

D3 メモ [基礎編]

D3

データを元にHTMLを操作するためのライブラリ。データを元にしたヴィジュアライズもできるし、jQueryの様な使い方で、動的なページを作ることもできる。

http://mbostock.github.com/d3/ex/

基本的な使い方(DOM操作)

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>d3 demo</title>
  <!--[if lt IE 9]>
  <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->
  <script src="js/libs/d3.v2.js" type="text/javascript"></script>
  <script src="js/app.js" type="text/javascript"></script>
</head>
<body onload="draw();">
  <div id="container"></div>
</body>
</html>
// app.js
function draw() {
  d3.select('#container')
    .append('p')
    .text('Hello world');
}

jQueryみたいにセレクタで要素を選択(select)し、メソッドのチェイニング(append, text)もできる。(セレクタに使えるのは、デフォルトだとSelectors API LV.1に準拠した文字列)


selectは、要素が複数見つかった場合には先頭の要素しか返さないので

  <div class="multiple"></div>
  <div class="multiple"></div>
  <div class="multiple"></div>
  <div class="multiple"></div>

の様な、複数の要素を選択したい場合にはselectAll()を使う。

  d3.selectAll('.multiple')
    .append('p')
    .text('Hello world');

データとDOMのバインド

上の例の様なjQuery的な使い方だけではなく、d3ではデータとDOM要素をバインドすることができる。

  // ダミーデータ
  var dummy = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

  d3.select('#container')
    .selectAll('li')     // 後で作る要素を選択しておく
    .data(dummy)         // バインドするデータを指定する
    .enter()                   // データと関連付けした要素を生成する(プレースホルダになる)
    .append('li')        // プレースホルダに要素を追加
    .text(function(d, idx) {
      // 各<li>にバインドされたデータをテキストノードとして挿入
      return 'index: ' + idx + ', data: ' + d;
    });

配列の要素数文のli要素が生成される。
データと要素が関連付けされているかどうかは、ブラウザのデベロッパーツール等で生成されたli要素をみると、"__data__"というキーに配列の要素が値としてバインドされている事が確認できる。

SVGを使う

データを使ってSVGでグラフィックを描く

var dummy = [4, 2, 6, 1, 8, 3, 5, 9, 5, 10];

  // SVG要素を作る
  var svg = d3.select('#container')
                     .append('svg')
                     .attr('width', 500)
                     .attr('height', 310);

  console.log(svg);

以下のようなHTMLができる

<div id="container">
  <svg width="500" height="310"></svg>
</div>

変数svgには生成したsvg要素(の配列)が入ってるので、selectやappendを使って更に要素を追加できる。

// データに応じた半径の円を描く
function draw() {
  var dummy = [4, 2, 6, 1, 8, 3, 5, 9, 5, 10],
      svg,
      w = 500,
      h = 310;

  // SVG要素を作る
  svg = d3.select('#container')
                .append('svg')
                .attr('width', w)
                .attr('height', h);

  svg.selectAll('circle')
       .data(dummy)
       .enter()
       .append('circle')
       .attr('cx', function(d, idx) {
          return idx * 20 + 5
       })
       .attr('cy', h / 2)
       .attr('r', function(d) {
         return d;
       })
       .attr('fill', function(d) {
         var color = '#0000ff';
         if(d > 5) {
           color = '#ff0000';
         }

         return color;
       });

  svg.selectAll('text')
       .data(dummy)
       .enter()
       .append('text')
       .text(function(d) {
         return d;
       })
       .attr('x', function(d, idx) {
         return idx * 20;
       })
     .attr('y', function(d, idx) {
       return h / 3 * 2;
     })
     .attr('font-size', '12px')
     .attr('fill', '#333');
  }