CAIM:03
図形描画関数をつくる
演習講師:渡邉 賢悟 (渡辺電気株式会社)
東京工科大学メディア学部
使用するプログラム
•
caim03_draw.zip
基礎演習(1-1) : 四角を描く関数をつくる
•
ImageToolBox.swiftのfillRect関数をつくる
•
fillColor関数に似ているが、画面全体ではなく「x1,y1,x2,y2の範囲」を塗る関数である
static
func
fillRect(
_
img:
CAIMImage
,
x1:Int, y1:Int, x2:Int, y2:Int, color:CAIMColor
) {
// 画像のデータを取得
let
mat = img.
matrix
// 画像のピクセルデータ
let
wid = img.
width
// 画像の横幅
let
hgt = img.
height
// 画像の縦幅
// 2点の座標値の小さい方、大きい方を判別して別の変数に入れる
let min_x:Int = min(x1, x2)
let min_y:Int = min(y1, y2)
let max_x:Int = max(x1, x2)
let max_y:Int = max(y1, y2)
// min_x~max_x, min_y~max_yまでの範囲を塗る
for
y
in
min_y ... max_y
{
for
x
in
min_x ... max_x
{
// x,yが画像外にはみだしたら、エラー防止のためcontinueで処理をスキップする
if(x < 0 || y < 0 || x >= wid || y >= hgt) { continue }
mat[y][x].
R
= color.
R
mat[y][x].
G
= color.
G
mat[y][x].
B
= color.
B
mat[y][x].
A
= color.
A
}
}
}
基礎演習(1-2) : fillRectをつかう
•
DrawingViewController.swiftのsetup関数内でfillRect関数を位置を変えて複数回使ってみよう
•
今回から画面全体のビュー
(view_all)と画面と同じサイズの画像(img_all)を用意し、img_allに描画する
class DrawingViewController: CAIMViewController
{
// view_allを画面いっぱいのピクセル領域(screenPixelRect)の大きさで用意
var view_all:CAIMView = CAIMView(pixelFrame: CAIM.screenPixelRect) // 画像データimg_allを画面のピクセルサイズ(screenPixelSize)に合わせて用意
var img_all:CAIMImage = CAIMImage(size: CAIM.screenPixelSize)
override func setup() {
// img_allを白で塗りつぶす
img_all.fillColor( CAIMColor.white ) // view_allの画像として、img_allを設定する
view_all.image = img_all
// view_allを画面に追加
self.view.addSubview( view_all )
// 四角をずらしながら6つ描く
ImageToolBox.fillRect(img_all, x1: 30, y1: 20, x2: 110, y2: 100,
color: CAIMColor(R: 1.0, G: 0.0, B: 0.0, A: 1.0) )
ImageToolBox.fillRect(img_all, x1: 130, y1: 20, x2: 210, y2: 100,
color: CAIMColor(R: 0.8, G: 0.0, B: 0.2, A: 1.0) )
ImageToolBox.fillRect(img_all, x1: 230, y1: 20, x2: 310, y2: 100,
color: CAIMColor(R: 0.6, G: 0.0, B: 0.4, A: 1.0) )
ImageToolBox.fillRect(img_all, x1: 330, y1: 20, x2: 410, y2: 100,
color: CAIMColor(R: 0.4, G: 0.0, B: 0.6, A: 1.0) )
ImageToolBox.fillRect(img_all, x1: 430, y1: 20, x2: 510, y2: 100,
color: CAIMColor(R: 0.2, G: 0.0, B: 0.8, A: 1.0) )
ImageToolBox.fillRect(img_all, x1: 530, y1: 20, x2: 610, y2: 100,
color: CAIMColor(R: 0.0, G: 0.0, B: 1.0, A: 1.0) ) }
}
x1, x2とCAIMColorのRGB値を
基礎演習(1-3) : 範囲の指定について
•
ImageToolBox.swiftのfillRect関数は描画対象の範囲を限定している
•
以下の
min_x, min_y, max_x, max_yがその範囲
•
引数で渡された
x1,x2、およびy1,y2について小さい値、大きい値を判別する
•
小さい方
(min_x,min_y)∼大きい方(max_x,max_y)をループの範囲として指定する
(これまでは
{0~wid-1, 0~hgt-1} = 画面全体であった)
// 2点の座標値の小さい方、大きい方を判別して別の変数に入れる
let min_x:Int = min(x1, x2)
let min_y:Int = min(y1, y2)
let max_x:Int = max(x1, x2)
let max_y:Int = max(y1, y2)
// 小さい方
∼
大きい方の範囲のループにすることで処理範囲を限定する
for
y
in
min_y ... max_y
{
for
x
in
min_x ... max_x
{
…(以下ループ割愛)…
基礎演習(1-4) : はみ出し防止処理
•
fillRect関数では引数x1,y1,x2,y2で描画範囲を自由に指定できる
•
その代わり、指定した座標値が画像データをはみ出す可能性がある
(mat[y][x]は範囲をはみ出すとエラーで止まる)
•
そこで画像からはみ出さないようにはみ出し防止の処理を入れる
•
赤字部分は
「はみ出したときの条件式」
•
はみだしたら
continue
を呼び出してスキップする(次のループ処理へ進む)
// min_x~max_x, min_y~max_yまでの範囲を塗る
for
y
in
min_y ... max_y
{
for
x
in
min_x ... max_x
{
// x,yが画像外にはみだしたら、エラー防止のためcontinueで処理をスキップする
if(x < 0 || y < 0 || x >= wid || y >= hgt) { continue }
mat[y][x].
R
= color.
R
mat[y][x].
G
= color.
G
mat[y][x].
B
= color.
B
mat[y][x].
A
= color.
A
}
}
基礎演習(2-1) : fillRectに透明度をつける
•
ImageToolBox.swiftのfillRect関数に
引数
opacity
を追加する
•
opacityにはデフォルト値として
=1.0
を入れておく
※opacity = 0.0(透明)∼1.0(完全不透明)
•
opacityを使って、元の色mat[y][x]と塗る色colorで混色計算を行う
•
mat[y][x].R = color.R * opacity + mat[y][x].R * (1.0-opacity)
static func fillRect(_ img:CAIMImage, x1:Int, y1:Int, x2:Int, y2:Int, color:CAIMColor, opacity:Float=1.0) { // 画像のデータを取得
let mat = img.matrix // 画像のピクセルデータ let wid = img.width // 画像の横幅
let hgt = img.height // 画像の縦幅
// 2点の座標値の小さい方、大きい方を判別して別の変数に入れる let min_x:Int = min(x1, x2)
let min_y:Int = min(y1, y2) let max_x:Int = max(x1, x2) let max_y:Int = max(y1, y2)
// min_x~max_x, min_y~max_yまでの範囲を塗る for y in min_y ... max_y {
for x in min_x ... max_x {
// x,yが画像外にはみだしたら、エラー防止のためcontinueで処理をスキップする if(x < 0 || y < 0 || x >= wid || y >= hgt) { continue }
mat[y][x].R = color.R * opacity + mat[y][x].R * (1.0-opacity) mat[y][x].G = color.G * opacity + mat[y][x].G * (1.0-opacity) mat[y][x].B = color.B * opacity + mat[y][x].B * (1.0-opacity) mat[y][x].A = color.A * opacity + mat[y][x].A * (1.0-opacity) }
} }
基礎演習(2-2) : fillRect(opacity付)を使う
•
DrawingViewController.swiftのsetup関数内でfillRect関数のopacity付きを使う
•
下記のように
for文と計算を用いて、複数の四角をimg_all上に描いてみる
class
DrawingViewController:
CAIMViewController
{
// view_allを画面いっぱいのピクセル領域(screenPixelRect)の大きさで用意
var
view_all:
CAIMView
=
CAIMView
(pixelFrame:
CAIM
.
screenPixelRect
)
// 画像データimg_allを画面のピクセルサイズ(screenPixelSize)に合わせて用意
var
img_all:
CAIMImage
=
CAIMImage
(size:
CAIM
.
screenPixelSize
)
override
func
setup() {
…(基礎演習1で記述済 : 割愛)…
// 透明度付きの四角を描く
for i in 0 ..< 6 {
ImageToolBox.fillRect(img_all, x1: 30+100*i, y1: 120, x2: 110+100*i, y2: 200,
color: CAIMColor(R: 1.0 - Float(i) * 0.2, G: 0.0, B: Float(i) * 0.2, A: 1.0),
opacity:0.8 - Float(i) * 0.1 )
}
}
}
基礎演習(3-1) : 円を描く
•
ImageToolBox.swiftにfillCircle関数を作ろう
•
引数は円の中心座標
(cx,cy)と半径(radius), 色(color), 不透明度(opacity)とする
static func fillCircle(_ img:CAIMImage, cx:Int, cy:Int, radius:Int, color:CAIMColor, opacity:Float=1.0) {
// 画像のデータを取得
let mat = img.matrix // 画像のピクセルデータ
let wid = img.width // 画像の横幅
let hgt = img.height // 画像の縦幅
// 処理の範囲を決める
let min_x:Int = cx - radius let min_y:Int = cy - radius let max_x:Int = cx + radius let max_y:Int = cy + radius
// min_x~max_x, min_y~max_yまでの範囲を塗る
for y in min_y ... max_y {
for x in min_x ... max_x {
// x,yが画像外にはみだしたら、エラー防止のためcontinueで処理をスキップする
if(x < 0 || y < 0 || x >= wid || y >= hgt) { continue }
// 中心点からの距離
let dist:Float = sqrt(Float((x-cx)*(x-cx)) + Float((y-cy)*(y-cy)))
// 中心点(cx, cy)からの距離が半径radius以内なら塗る
if( dist <= Float(radius) ) {
mat[y][x].R = color.R * opacity + mat[y][x].R * (1.0-opacity) mat[y][x].G = color.G * opacity + mat[y][x].G * (1.0-opacity) mat[y][x].B = color.B * opacity + mat[y][x].B * (1.0-opacity) mat[y][x].A = color.A * opacity + mat[y][x].A * (1.0-opacity) }
} }
基礎演習(3-2) : fillCircleを使う
•
DrawingViewController.swiftのsetup関数内でfillCircle関数を使ってみる
•
opacityを設定しない使い方、設定した使い方をしてみよう
class
DrawingViewController:
CAIMViewController
{
// view_allを画面いっぱいのピクセル領域(screenPixelRect)の大きさで用意
var
view_all:
CAIMView
=
CAIMView
(pixelFrame:
CAIM
.
screenPixelRect
)
// 画像データimg_allを画面のピクセルサイズ(screenPixelSize)に合わせて用意
var
img_all:
CAIMImage
=
CAIMImage
(size:
CAIM
.
screenPixelSize
)
override
func
setup() {
…(基礎演習1,2で記述済 : 割愛)…
// 丸を描く
for i in 0 ..< 6 {
ImageToolBox.fillCircle(img_all, cx: 70+100*i, cy: 260, radius: 40,
color: CAIMColor(R: 1.0, G: 0.5 + Float(i) * 0.1, B:Float(i) * 0.1, A: 1.0))
}
// 丸を描く(透明度付き)
for i in 0 ..< 6 {
ImageToolBox.fillCircle(img_all, cx: 70+100*i, cy: 360, radius: 40,
color: CAIMColor(R: 1.0, G: 0.5, B: 0.0, A: 1.0),
opacity:1.0 - Float(i) * 0.15 )
}
}
}
基礎演習(3-3) : 円の内部判定
•
fillCircle関数でもループ自体は「四角」で行っている
•
ループ内で
if文「円の内部のみ塗る」よう処理すれば円が描ける
•
処理対象の座標
(x,y)の、円の中心(cx,cy)からの距離distを計算する
•
distが半径radius内 = 円の内部 → 塗る
// 中心点からの距離
let dist:Float = sqrt(Float((x-cx)*(x-cx)) + Float((y-cy)*(y-cy)))
// 中心点(cx, cy)からの距離が半径radius以内なら塗る
if
(
dist <= Float(radius)
) {
mat[y][x].
R
= color.
R
* opacity + mat[y][x].
R
* (
1.0
-opacity)
…(上記のようにG,B,Aの色指定: 以下割愛)…
基礎演習(3-4) : fillCircleで円を重ねる
•
DrawingViewController.swiftのsetup関数内でfillCircle関数で
透けた円が重なるように
img_allに描画してみよう(opacityを小さくしておく)
class DrawingViewController:
CAIMViewController
{
// view_allを画面いっぱいのピクセル領域(screenPixelRect)の大きさで用意
var
view_all:
CAIMView
=
CAIMView
(pixelFrame:
CAIM
.
screenPixelRect
)
// 画像データimg_allを画面のピクセルサイズ(screenPixelSize)に合わせて用意
var
img_all:
CAIMImage
=
CAIMImage
(size:
CAIM
.
screenPixelSize
)
override func setup() {
…(基礎演習1,2で記述済 : 割愛)…
…(基礎演習3-2で記述済 : 割愛)…
// 丸を重ねて描く
for i in 0 ..< 12 {
ImageToolBox.fillCircle(img_all, cx: 70+45*i, cy: 460, radius: 40,
color: CAIMColor(R: 1.0, G: 0.2, B: 0.2, A: 1.0),
opacity:0.3 - Float(i) * 0.02 )
}
}
}
発展演習(1-1) : グラデーション円を描く
•
ImageToolBox.swiftにfillDome関数を作ろう
•
fillCircle関数をCopy&Pasteして書き直すとよい
static func fillDome(_ img:CAIMImage, cx:Int, cy:Int, radius:Int, color:CAIMColor, opacity:Float=1.0) { // 画像のデータを取得
let mat = img.matrix // 画像のピクセルデータ let wid = img.width // 画像の横幅
let hgt = img.height // 画像の縦幅 // 処理の範囲を決める
let min_x:Int = cx - radius let min_y:Int = cy - radius let max_x:Int = cx + radius let max_y:Int = cy + radius for y in min_y ... max_y {
for x in min_x ... max_x {
if(x < 0 || y < 0 || x >= wid || y >= hgt) { continue }
let dist:Float = sqrt(Float((x-cx)*(x-cx)) + Float((y-cy)*(y-cy)))
if( dist <= Float(radius) ) {
// 中心からの距離でcosを計算してαとして用いる
var alpha = Float(cos(Double(dist) / Double(radius) * Double.pi / 2.0)) // αに不透明度opacityを掛ける
alpha *= opacity
mat[y][x].R = color.R * alpha + mat[y][x].R * (1.0-alpha) mat[y][x].G = color.G * alpha + mat[y][x].G * (1.0-alpha) mat[y][x].B = color.B * alpha + mat[y][x].B * (1.0-alpha) mat[y][x].A = color.A * alpha + mat[y][x].A * (1.0-alpha) }
} }
発展演習(1-2) : fillDomeを使う
•
DrawingViewController.swiftのsetup関数内でfillDome関数を使ってみる
•
img_all上にフワッとした円が描かれることを確認する
class
DrawingViewController:
CAIMViewController
{
// view_allを画面いっぱいのピクセル領域(screenPixelRect)の大きさで用意
var
view_all:
CAIMView
=
CAIMView
(pixelFrame:
CAIM
.
screenPixelRect
)
// 画像データimg_allを画面のピクセルサイズ(screenPixelSize)に合わせて用意
var
img_all:
CAIMImage
=
CAIMImage
(size:
CAIM
.
screenPixelSize
)
override
func
setup() {
…(基礎演習1,2で記述済 : 割愛)…
…(基礎演習3-2で記述済 : 割愛)… …(基礎演習3-4で記述済 : 割愛)…