반응형

가끔 아니 어쩌면 자주 3D 공간안에서 물체의 위치를 지정할때 이 물체가 저쪽에 닿는 면에 딱 위치하고 싶은데... 할때가 있죠? 

예를 들면 나무나 돌덩이 이런걸 랜덤하게 막 생성한 다음 굴곡진 지형에 모두 딱 안착시키고 싶다거나요.

또는 어떤 물체를 기준으로 어떤 방향으로 뻗어 나갈때 무엇인가 닿는 물체가 있는지 검사하고 싶을때도 있지요.

이때 사용되는 개체가 바로 Ray 입니다.

실제로 Ray를 알기 전과 알고난 뒤는 정말 다른 세계라고 할수 있겠습니다.

 

자 오늘은 Ray 에 대하여 알아보도록 하겠습니다.

3D 는 정말 놀라울 정도로 기하학적인 수학의 결정체라 할 수 있겠습니다. 이를 구현하기 위하여 정말 얼마만큼의 수학적 노력이 있었는지 상상이 안될 정도 입니다.

오늘은 그 결정체중의 하나인 Ray에 대하여 알아볼텐데요. 3DS MAX 에서 Ray 는 두가지 명령어를 통해 사용됩니다.

intersectRay 와 intersectRayEx, intersectRayScene 이렇게 세가지 입니다. 사용하는 목적에 따라 구분하여 사용하시면 되는데요. 각각은 아래와 같습니다.

  • intersectRay : Ray 와 대상물체를 지정한 뒤 부딧히는 위치를 알아낸다.
  • intersectRayEx : 위와 동일하며 부딪힌 Face 정보와 부딪힌 면의 normal 정보를 돌려준다
  • intersectRayScene : 대상 개체를 지정하지 않고 Scene 내의 어떤 물체든 부딪힌 정보를 돌려준다. (느림)

사용하는 방법은 간단한데요, 먼저 Ray 를 생성해주고 필요하면 Ray의 출발 위치(pos)와 방향(dir)을 변경할 수 있습니다.

myRay = ray [0,0,0] [0,0,-1]

ray 를 생성하는 구문 인데요, [0,0,0] 위치를 출발점으로 하고 [0,0,-1] 을 방향으로 하는 ray 가 만들어 지게 됩니다. [0,0,-1] 이라 하면 z 축을 기준으로 아래쪽을 바라보는 방향이라고 보면 되겠죠? 

 

이렇게 만들어진 Ray를 위의 명령어와 함께 사용해볼까요?

임시로 테스트용 Scene 을 만들어 봄

먼저 위와 같은 Scene 을 만들어서 테스트 해보려 합니다. 노란색 공이 지면에 부딪히는 위치로 이동 시킬거에요. 중간에 네모 박스가 있지만 네모박스는 무시하고 지면에 위치시켜야 한다면 아래와 같이 intersectray 를 이용하여 위치시킬 수 있습니다.

-- ray 를 생성해 준다
myRay = ray [0,0,0] [0,0,-1]

-- 이동시킬 물체를 지정하자
obj = $Sphere001

-- ray 의 출발 위치를 물체의 중심으로 설정해보자
myRay.pos = obj.pos

-- 위치 검출에 사용될 지형을 지정해보자
tern = $terrain_01

-- intersectray 를 이용하여 지형과 부딪히는 위치를 찾아내자
interResult = intersectray tern myRay

-- 어떤 결과가 나왔는지 print 를 이용해서 살펴보자
print(interResult)

-- 부딪힌 위치에 오브젝트를 위치시키자
obj.pos = interResult.pos

요렇게 하면 어떻게 되냐면요

지면에 정확하게 안착함

요렇게 노란 공이 지면에 달라 붙게 됩니다. 코드에 주석을 달아 놓았으니 따로 설명은 안드려도 될 것 같은데요. 한가지만 설명해 드리자면 intersectray의 결과값을 보면 (ray [-1.62331,11.1008,1.22501] [-0.407511,-0.107746,0.906822]) 요런 식으로 ray 의 형식으로 결과가 나옵니다. ray 는 pos 와 dir 의 속성이 있지 않습니까?? 여기서 결과값의 pos 정보가 바로 부딧힌 면의 위치 정보가 됩니다. 그 뒤에 나오는 [-0.407511,-0.107746,0.906822] 는 부딪힌 면의 normal 값으로 만약 공이 부딪힌 다음 튕겨나간다거나 하는 다음 동작이 필요하다면 반드시 필요한 정보이지요.

어쨌든 intersectray 만 보자면 부딪힌 위치와 각도를 ray 의 형태로 돌려준다는 것을 알 수 있었습니다. 특징이라 하면 , 특정 개체를 대상으로 하기 때문에 속도가 매우 빠릅니다. 사실상 성능을 고려하지 않아도 되는 함수죠.

그런데 만약 부딪힌 면이 어디인지 알아야 하고 부딪힌 개체에도 뭔가 추가적인 액션이 필요할 수 있겠습니다. 사람일은 모르는거자나요? 누군가는 저 지형개체에 어떤 face에 물체가 부딪혔는지를 알아야 할수도 있지 않겠습니까? 이 때 intersectrayex 라는 함수를 이용하게 됩니다.

이번에는 intersectray 대신에 intersectrayex 를 사용해 볼 겁니다. 사용방법은 동일하고요. intersectrayEx [node] [ray] 와 같이 사용해 주면 됩니다.

-- intersectray 를 이용하여 지형과 부딪히는 위치를 찾아내자
interResult = intersectrayex tern myRay

-- 어떤 결과가 나왔는지 print 를 이용해서 살펴보자
print(interResult)

-- 결과는? 
#((ray [-1.62331,11.1008,1.22501] [-0.382998,-0.128105,0.914823]), 240, [0.383149,0.282177,0.334673])

아까와는 결과가 조금 다르죠? #(oo,oo,oo) 형태로 나온걸 보면 배열로 결과가 나온걸 알 수 있습니다. 총 3개의 리턴값이 나왔는데요.

  • (ray [-1.62331,11.1008,1.22501] [-0.382998,-0.128105,0.914823])
  • 240
  • [0.383149,0.282177,0.334673]

intersectray 와 마찬가지로 첫번째 결과에는 부딪힌 면의 위치, 방향을 담는 ray 정보가, 두번째에는 어떤 숫자가 나왔고요, 세번째에는 point3 형식으로 좌표가 나왔는데, 딱보니 위에 ray 에 부딪힌 면의 방향이라는 것을 알 수 있겠습니다. 두번째 숫자는 뭐냐고요?

부딪힌 face 의 index 임을 알 수 있다.

바로 부딪힌 mesh 의 face 인덱스 입니다. 240 번이죠? 바로 저부분에 부딪힌 것 입니다. (이번에는 이동은 하지 않았기 때문에..) 만약 동일하게 이동해야 한다면 배열 안의 개체에서 위치 정보를 뽑아내야 하므로 interResult[1].pos 와 같은 방법으로 부딪힌 면의 위치를 가져오면 됩니다.

이런 기능을 이용해서 부딪힌 면의 색상을 바꾸어 준다거나 해당 face를 삭제하는 등의 기능을 넣을 수 있겠지요? 뭐 레이저총에 맞은 face 가 폭파되며 사라지는 등의...  네.. 그럴때 꼭 필요한 훌륭한 명령어 입니다.

참고로 intersectray 나 intersectrayex 는 mesh 오브젝트에서 정확한 정보를 받아 올 수 있습니다. 가끔 기본 primatine 에 modifier 로 edit_poly 같은것을 씌운 경우에는 동작하지 않을 수 있으니 만약 결과값이 undefined 와 같은 값이 나온다면 검출에 사용된 모델링을 mesh 로 변경한 뒤 (convertTomesh obj) 검사를 진행하시면 됩니다.

 

끝으로 intersectRayScene 은 위와는 다르게 Ray 가 어떤 개체에 부딪히는지 알수 없을 때 사용합니다.

모두 동일하고 해당 명령만 intersectRayScene 으로 변경해서 돌려볼까요?

interResult = intersectrayScene myRay

요렇게 말이죠. 이번에는 대상을 특정하지 않기 때문에 함수 뒤에 인자가 ray 개체 하나만 들어갔습니다. 

결과를 볼까요? 

#(#($Editable_Mesh:terrain_01 @ [-4.616531,-2.612816,0.000000], (ray [-1.62331,11.1008,1.22501] [-0.382998,-0.128105,0.914823])), #($Sphere:Sphere001 @ [-1.623309,11.100846,65.260162], (ray [-1.62331,11.1008,61.9389] [0,0,-1])), #($Box:Box001 @ [15.646946,18.567043,19.666662], (ray [-1.62331,11.1008,26.4499] [0,0,1])))

뭔가 무시무시하게 길게 나왔죠?

이녀석도 역시 배열 개체이므로 하나씩 보면 

#($Editable_Mesh:terrain_01 @ [-4.616531,-2.612816,0.000000], (ray [-1.62331,11.1008,1.22501] [-0.382998,-0.128105,0.914823]))
#($Sphere:Sphere001 @ [-1.623309,11.100846,65.260162], (ray [-1.62331,11.1008,61.9389] [0,0,-1]))
#($Box:Box001 @ [15.646946,18.567043,19.666662], (ray [-1.62331,11.1008,26.4499] [0,0,1]))

요렇게 3개의 개체와 부딪혔다는 정보를 돌려준 것을 알 수 있습니다. 심지어는 본인(sphere) 에도 부딪힌 정보를 돌려 받았다는 것을 알 수 있네요 ㄷㄷㄷ. (저걸 원한건 아닌데) 부딪힌 정보를 보면 역시 내부 정보 역시 배열인데요, 첫번째는 부딪힌 대상 개체, 두번째는 부딪힌 위치정보 (ray 속성) 라는걸 알 수 있습니다.

만약 개체에 어떤 특정할만한 속성이 있다면 해당 개체에 물체를 위치 시킬 수 있습니다.

-- ray 를 생성해 준다
myRay = ray [0,0,0] [0,0,-1]

-- 이동시킬 물체를 지정하자
obj = $Sphere001

-- ray 의 출발 위치를 물체의 중심으로 설정해보자
myRay.pos = obj.pos

-- 위치 검출에 사용될 지형을 지정해보자
tern = $terrain_01

-- intersectray 를 이용하여 지형과 부딪히는 위치를 찾아내자
interResult = intersectrayScene myRay

-- 어떤 결과가 나왔는지 print 를 이용해서 살펴보자
print(interResult)

for i = 1 to interResult.count do
(
	testObj = interResult[i][1]
	if (findString testObj.name "Box") != undefined then
	(
		obj.pos = interResult[i][2].pos
	)
)

요렇게요. 

코드에서는 Box 라는 이름을 포함하는 개체를 만나면 해당 개체에 위치하도록 하는 코드 입니다.

이게 사실 한두개일 때는 간단한 이야기 인데 지면에 위치 시켜야 하는 개체가 수십개 ~ 수백개가 되면 일이 커집니다. 그럴때는 스크립트가 필요한 시점인거죠.

예를 들면 이렇게요..

이 많은 공들을 지면에 붙이라고?

 

모두 sphere 라는 이름을 갖고 있기때문에 간단하게 처리할 수 있겠네요. 지면에 닿는 개체는 지면에 닿도록 하고 box 에 닿는 개체는 box 에 위치시키도록 하겠습니다. 

sphere 의 중심을 기준으로 하다보니 약간 파뭍히는 대상이 있었는데요, sphere 의 반지름 만큼 위쪽으로 위치하도록 코드를 짜 보겠습니다.

-- ray 를 생성해 준다
myRay = ray [0,0,0] [0,0,-1]

-- 이름이 sphere 로 시작되는 모든 개체를 변수 objs 에 담자.
objs = execute("$Sphere*")

for obj in objs do
(
	-- 각 sphere 의 중심점으로 ray 의 위치를 지정함
	myRay.pos = obj.pos
	
	-- intersectray 를 이용하여 지형과 부딪히는 위치를 찾아내자
	interResult = intersectrayScene myRay


	for i = 1 to interResult.count do
	(
		testObj = interResult[i][1]
		if (findString testObj.name "Box") != undefined then
		(
			obj.pos = interResult[i][2].pos
			-- 파묻히지 않도록 반지름 만큼 위로 올림
			obj.pos.z += obj.radius
		)
		else if (findString testObj.name "terrain") != undefined then
		(
			--- 부딪힌 정보가 terrain 일때 해당 위치로 이동 시킴 
			obj.pos = interResult[i][2].pos
		-- 파묻히지 않도록 반지름 만큼 위로 올림
			obj.pos.z += obj.radius
		)
	)
)

 

뭐 별거 없죠? 위에서 설명한 것을 반복문만 추가했을 뿐 입니다.

결과는요?

한번에 모두 지면에 안착 성공

요렇게 예쁘게 안착된 것을 확인 할 수 있습니다.

물론 저는 본 기능을 설명 드리기 위해 위와 같은 예제를 들었지만요. 실제 제가 사용하는 함수에는 몇초만에 수천번 이상 위치를 검출하며 ray 가 사용되는 케이스가 허다분 합니다.

저는 주로 자동으로 모델링을 할때 모델링의 위치, 크기, 배치, 부딪힘, 확장 범위 등을 검사할때 사용하는데요, 이런 검사들을 통해 사람이 손을 데지 않고도 아주 정교한 모델링을 할 수 있는 근간이 됩니다.

참고로 intersectRayScene 의 경우 scene 의 모든 개체에 대하여 검사가 이루어지기 때문에 Scene 내에 아주 많은(수만개 이상의) node 들이 자리잡고 있는 경우에는 속도가 매우 느려질 수 있습니다. 그래서 시작 위치를 기준으로 반경내에 들어오는 대상을 먼저 골라낸 다음 속도가 빠른 intersectRay 를 이용하여 각각 부딪힌 위치를 검출하는 방식으로 사용하고 있습니다. 프로그래밍이라는게 그렇자나요. ㅎ 높은 성능과 효율성을 위해 다양하고 창의적인 방법으로 접근해 가는게 바로 프로그래밍의 묘미이지요.

오늘은 이만 마칩니다. 내용이 길어기니 힘드네요 ㅋ

2021.03.24 - [DEV/MAX SCRIPT] - 3DS MAX 스크립트 레퍼런스 (헬프파일) 다운 받기

 

3DS MAX 스크립트 레퍼런스 (헬프파일) 다운 받기

스크립트 초급자든 중급자든 반드시 필요한 것 하나를 꼽으라면 저는 레퍼런스라고 생각합니다. 기존에는 3DS MAX 설치 시에 함께 설치되어 스크립트 작성 창에서 F1 키를 누르면 chm 파일 형식의

diy-dev-design.tistory.com

2021.02.24 - [DEV/MAX SCRIPT] - 3da max script 점과 점 사이의 거리 구하기

 

3da max script 점과 점 사이의 거리 구하기

간단하게 포스팅을 남기려고 합니다. 제목 그대로 버텍스 간 거리 구하기 입니다. 아니 정확히는 두개의 point3 사이의 거리를 구하는 방법을 소개해 드릴까 합니다. 그리고 용용 편으로 두 점사

diy-dev-design.tistory.com

2020.04.29 - [DEV/MAX SCRIPT] - 3DS MAX 스크립트로 개체 선택하기

 

3DS MAX 스크립트로 개체 선택하기

Max Script 를 이용하여 무엇인가를 하려면 필수 요소라고 할 수 있는 것 중의 하나가 개체를 선택하거나 지정하는 방법입니다. 여기서 개체는 3D Scene 안의 오브젝트, 스플라인, 헬퍼 등과 같은 사

diy-dev-design.tistory.com

 

반응형
반응형

코로나로 인하여 아이들이 집밖에 나가지도 못하고 집안에서 게임만 하고 있는 것을 보고 아이들을 위해 놀이거리를 하나 만들어 주어야겠다고 생각하고 시작한 동물의 숲 카드 만들기 입니다.

일단 물고기 카드를 만들려고 시작했는데 종류가 80 개나 되다 보니 만만치가 않았습니다.

그래서 가능하면 쉽게 빠르게 만들 수 있고 필요하면 나중에 수정하기도 쉽도록 코딩을 좀 하는 방향으로 제작 방안을 결정했습니다.

 

포토샵 자동화 과정이 이루어지는 전체 과정을 엿볼 수 있는 좋은 기회이니 관심 있으신 분은 주의 깊게 보시면 도움이 될 것이라 생각합니다.

 

 

 

어떻게 만들지?

 

일단 디자인을 결정해야 겠죠? 몇가지 구상 끝에 아래와 같이 만들기로 합니다.

이름과 가격, 잡히는 곳을 적고 그림자를 표시한뒤 프린트 해서 접으면 끝

만들어진 이미지를 출력하여 반으로 접어 붙이면 앞면은 물고기 그림자, 뒷면은 물고기 정보가 담겨있는 카드가 만들어 집니다.

 

제작은 다음과 같이 진행하기로 하였습니다.

  • 모든 동물의 이름, 사는곳, 위치를 물고기 이미지와 함께 레이어 명에 남긴다.
  • 배경의 타입에 맞게 배경이미지를 제작한다 (연못, 강, 바다 등)
  • 물고기의 그림자의 크기별로 그림자 이미지를 별도로 만든다.
  • 스크립트를 작성한다.
    • 물고기 레이어를 탐색하면서 레이어 명을 분석한다.
    • 레이어명에서 추출된 배경, 그림자 레이어를 on 한다
    • 정보에 맞도록 텍스트 레이어의 내용을 변경한다
    • 이미지를 파일로 저장한다
    • 물고기 레이어, 배경, 그림자 레이어를 숨긴다.

요런 과정을 통하여 만들기로 합니다.

 

 

먼저 포토샵으로 자동화를 위한 데이터 파일을 제작합니다.

먼저 물고기 이미지를 취득합니다.

일단 닌텐도에서 캡쳐한 이미지와 아직 잡지 못한 물고기는 인터넷에서 구한 뒤 레이어별로 이미지를 적재합니다.

레이어의 이름은 위에서 정의한 것 처럼 물고기의 정보를 담도록 합니다.

레이어별 정보로 설정한 명칭

 

그 다음 물고기 그림자 세트를 shadow 라는 레이어 셋 안에 만들어 줍니다.

물고기 그림자 크기별 모양

 

그리고 화면 레이아웃에 맞도록 텍스트 및 텍스트 박스, 기타 디자인을 완성합니다.

텍스트 레이아웃

 

강, 연못, 바다 등과 같이 배경에 맞도록 상단의 배경을 레이어로 구분하여 구성합니다.

잡히는 곳에 해당하는 이미지

 

총 6개의 배경이 구성이 되었습니다.

각각 물고기 레이어에서 정의한 장소와 동일하게 레이어 명칭을 설정해 줍니다.

그리고 물고기 레이어, shadow, 등을 레이어 셋 (폴더)로 구성합니다.

최종 레이어 구성

일단 이렇게 하면 PSD 파일은 준비가 완료된 셈 입니다.

 

 

 

 

이제 코딩을 해볼까요? 

먼저 저장할 경로와 파일 옵션을 설정해야 하는데요. 저는 출력용이므로 eps 포멧을 사용하였습니다.

var sPath = "C:\\Users\\cindy\\Pictures\\animal_tree\\eps\\"

var epsOption = new EPSSaveOptions()
epsOption.embedColorProfile = true

 

그 다음 현재 도큐먼트와 각 타입별 레이어 셋을 변수에 할당해 줍니다.

var cDoc = app.activeDocument;

var fLayer = cDoc.layers["fishs"].layers
var sLayers = cDoc.layers["shadow"].layers
var tLayer = cDoc.layers["text"].layers

 

이제 물고기 레이어 (fLayers) 를 한바퀴 돌며 저장을 할겁니다. 이때 레이어의 명칭을 parsing 해야 하는데요. 저는 _ 를 이용하여 구분자를 만들어 놓았으므로 아래와 같은 방법으로 문자열을 분리합니다.

for (var k = 0 ; k < fLayers.length; k++)//fLayer.layers.length
{
   
    var cLyr = fLayers[k]
    var tmpName = cLyr.name
    var nameSet = tmpName.split("_")
    
}

이렇게 하면 모든 fishs 레이어 안의 레이어에 대하여 nameSet 이라는 변수에는 현재 물고기 레이어 이름을 "_" 로 구분하여 배열로 담게 됩니다.

각 반복시마다 cLyr 라는 변수는 현재 레이어를 지칭하게 되죠.

이제 각 설정들을 변경하고 필요한 레이어를 켜거나 끄는 동작을 넣어야 합니다.

for (var k = 0 ; k < fLayers.length; k++)//fLayer.layers.length
{
   
    var cLyr = fLayers[k]
    var tmpName = cLyr.name
    var nameSet = tmpName.split("_")
    var bglayerName = nameSet[3]
    
    if (bglayerName == "바다(비)")
    {
        bglayerName = "바다"
    }
    //물고기 레이어 켜기
    cLyr.visible = true
    
    // 그림자 레이어 켜기
    sLayers[nameSet[2]].visible = true
    
    // BG 레이어 켜기
    cDoc.layers[bglayerName].visible = true
    
    // 텍스트 레이어 내용 변경
    tLayers[0].textItem.contents = nameSet[1]
    tLayers[1].textItem.contents = nameSet[3]
    tLayers[2].textItem.contents = nameSet[4]
    
    // eps 저장
    var dFile = new File(sPath + nameSet[0] + "_" + nameSet[1] + ".eps")
    cDoc.saveAs(dFile, epsOption, true, Extension.LOWERCASE)  
    // 그림자 레이어 끄기
    sLayers[nameSet[2]].visible = false
    
    //물고기 레이어 끄기
    cLyr.visible = false
    
    // BG 레이어 끄기
    cDoc.layers[bglayerName].visible = false
}

현재 물고기 레이어와 각 설정에 맞는 그림자 레이어를 켜고, 배경을 켠다음 텍스트 정보를 업데이트 합니다.

그리고 저장하기로 한 파일로 저장을 하게되죠. 

저장을 마치면 다시 켜주었던 레이어들은 꺼주면 한 번의 반복문이 완성되는 것이죠.

전체 코드를 보면 아래와 같습니다.

var sPath = "C:\\Users\\cindy\\Pictures\\animal_tree\\"
var pngOption = new PNGSaveOptions()
    pngOption.embedColorProfile = true;
    pngOption.formatOptions = FormatOptions.STANDARDBASELINE;
    pngOption.matte = MatteType.NONE;
    pngOption.quality = 100;
    pngOption.PNG8 = false; //24 bit PNG
    pngOption.transparency = true;
    pngOption.interlaced = true;
    
var sPath = "C:\\Users\\cindy\\Pictures\\animal_tree\\"

var epsOption = new EPSSaveOptions()
epsOption.embedColorProfile = true




var cDoc = app.activeDocument;


var fLayers = cDoc.layers["fishs"].layers
var sLayers = cDoc.layers["shadow"].layers
var tLayers = cDoc.layers["text"].layers

//alert(fLayer.layers.length);

for (var k = 0 ; k < fLayers.length; k++)//fLayer.layers.length
{
   
    var cLyr = fLayers[k]
    var tmpName = cLyr.name
    var nameSet = tmpName.split("_")
    var bglayerName = nameSet[3]
    
    if (bglayerName == "바다(비)")
    {
        bglayerName = "바다"
    }
    //물고기 레이어 켜기
    cLyr.visible = true
    
    // 그림자 레이어 켜기
    sLayers[nameSet[2]].visible = true
    
    // BG 레이어 켜기
    cDoc.layers[bglayerName].visible = true
    
    // 텍스트 레이어 내용 변경
    tLayers[0].textItem.contents = nameSet[1]
    tLayers[1].textItem.contents = nameSet[3]
    tLayers[2].textItem.contents = nameSet[4]
    
    // png 저장 - png 저장을 하려면 모드를 RGB 로 바꾸어 주어야 한다.
    //var dFile = new File(sPath + nameSet[0] + "_" + nameSet[1] + ".png")
    //cDoc.saveAs(dFile, pngOption, true, Extension.LOWERCASE)  
    
    // eps 저장 - eps 저장을 하려면 모드를 CMYK 로 바꾸어 주어야 한다.
    var dFile = new File(sPath + nameSet[0] + "_" + nameSet[1] + ".eps")
    cDoc.saveAs(dFile, epsOption, true, Extension.LOWERCASE)  
    
    // 그림자 레이어 끄기
    sLayers[nameSet[2]].visible = false
    
    //물고기 레이어 끄기
    cLyr.visible = false
    
    // BG 레이어 끄기
    cDoc.layers[bglayerName].visible = false
}

이렇게 물고기 레이어만큼 반복을 한뒤 코드는 종료 됩니다.

80개 레이어를 각각 설정하고 저장하는데 1분도 채 걸리지 않습니다. ^^

 

자동으로 추출된 동물의 숲 카드 이미지들

출력을 위하여 eps 포멧으로 저장하긴 했지만 윈도우에서 미리보기를 위하여 png 파일로도 한번 저장해 봤습니다.

예상 하였던 것처럼 잘 저장이 되었습니다.

 

 

 

출력용 레이아웃 파일 제작하기

끝으로 이제 출력용으로 레이아웃을 만들어 주어야 하는데요. 

일러스트 레이터에서 대지를 만들고 배치할 위치에 맞도록 가이드 선을 잡아 줍니다. 물론 png 로 저장한 뒤 파워 포인트 등으로 작업하셔도 됩니다.

그리고 이미지 하나하나 앉혀주면 됩니다.

번거로운 작업이지만 이렇게 딱 1회성 작업이 이루어지는 경우 그냥 손으로 하는게 나은 경우도 많습니다.

일러스트레이터에서 배치한 모여라 동물의 숲 카드 인쇄용 파일

여기서 중요한 것은 일러스트에서 이미지를 가져올 때 place(가져오기) 기능을 이용해야 하며 link (연결) 된 이미지로 불러와야 한다는 점 입니다.

link 된 이미지로 가져오게 되면 아래와 같이 선택 시 이미지에 X 표시가 되며 수정이 불가능한데요. 원본 eps 파일이 수정되는 경우 자동으로 이미지가 업데이트 되는 특징이 있습니다. 연결된 파일이 아닌 경우 수정이 되면 모든 이미지를 다시 가져와야 하는 불상사가 일어날 수 있습니다.

link 된 이미지(좌측)와 포함된 이미지(우측) 차이

 

이렇게 해서 모든 이미지를 앉히고 출력도 성공적으로 마칠 수 있었습니다.

만들어진 ai 파일을 이용하여 pdf 를 만들어 보았습니다.

동물의숲카드_물고기.z01
10.00MB
동물의숲카드_물고기.z02
10.00MB
동물의숲카드_물고기.zip
6.19MB

 

용량 관계상 분할 압축을 하였는데요. 다운 받으시고 압축을 해제하여 보시면 pdf 로 예쁘게 만들어져 있는 것을 보실 수 있습니다.

 

짧지 않은 시간이었지만 이렇게 해서 아이들이 가지고 놀 수 있는 동물의 숲 물고기 놀이 카드가 완성이 되었습니다.

아이들과 너무 재미있게 놀았기에 고생한 보람을 느낍니다.

이제 자동화를 위한 틀이 만들어 졌으므로 해산물 카드라든지, 다른 것들도 쉽게 추가 할 수 있을 것으로 생각합니다.

꼭 동물의 숲 카드 만들기가 아니더라도 포토샵을 이용하여 다양한 속성과 정보를 변경해 가면서 이미지를 제작해야 하는 경우라면 참고하실만한 레퍼런스가 되기를 바랍니다.

 

감사합니다.

 

실제 동물의 숲 카드 놀이를 하는 내용은 아래 포스트를 참고해 주세요

2021/01/02 - [DIY] - [아빠가 만든][집콕놀이] '동물의 숲' 카드 놀이 만들기

 

[아빠가 만든][집콕놀이] '동물의 숲' 카드 놀이 만들기

코로나로 인해 수업도 온라인으로 하고 저 역시 재택근무를 하고 있습니다. 요즘 코로나 감염 수준이 높아져 2.5 단계로 거리두기가 격상 되면서 아이들이 집안에만 머무르게 되고 날씨까지 추

diy-dev-design.tistory.com

2019/12/04 - [DEV/Adobe Script] - [포토샵스크립트] Instagram 용 파노라마 사진 자르기

 

[포토샵스크립트] Instagram 용 파노라마 사진 자르기

Instagram, 요즘 가장 핫한 SNS 가 아닐까 싶습니다. 지하철에서 무심코 주변을 보니 눈에 보이는 핸드폰 화면 마다 Instagram 화면이 보이는 것을 보고 깜짝 놀랐습니다. 난 안하는데... 어쨌든 Instagram

diy-dev-design.tistory.com

2019/06/10 - [DEV/Adobe Script] - [포토샵 스크립트] 강좌, 무작정 따라해보기

 

[포토샵 스크립트] 강좌, 무작정 따라해보기

어도비 포토샵은 자체 스크립트를 이용하여 다양한 기능을 수행할 수 있는 툴을 지원합니다. 언어는 자바 스크립트 이며 작성된 자바스크립트를 포토샵에서 실행하는 방법과 Extend Script Tool Kit

diy-dev-design.tistory.com

2019/12/13 - [DEV/Adobe Script] - [포토샵스크립트] PSD 모든 레이어 자동 저장, 하위 폴더 포함 (레이어 크기로 이미지 저장)

 

[포토샵스크립트] PSD 모든 레이어 자동 저장, 하위 폴더 포함 (레이어 크기로 이미지 저장)

안녕하세요. 제가 포토샵 스크립트를 이용하여 PSD 파일내의 모든 레이어를 자동으로 하위폴더 구조를 유지하며 저장할 수 있는 스크립트를 만들어서 올렸었는데요. 바로 아래 링크에서요. https:

diy-dev-design.tistory.com

 

2020/12/17 - [DIY] - [아빠의요리] 코로나 집콕 간식 '감동란' 만들어 주기

 

[아빠의요리] 코로나 집콕 간식 '감동란' 만들어 주기

TV 를 켜면 맨날 쉐프들이 나와서 멋들어진 말솜씨와 요리솜씨로 눈을 자극하는데요, 요즘 시대엔 아빠들도 약간의 요리쯤 할수 있어야 하지 않을까? 싶어서 포스팅을 합니다. 요즘 코로나로 인

diy-dev-design.tistory.com

 

반응형
반응형

저는 회사에서 디자인팀에 있고 전공도 디자인과 출신이며 심지어는 고등학교도 예체능계열 고등학교를 나왔습니다만 지금 회사에서 하는일의 95% 정도는 개발을 하고 있습니다. 실제로 제가 포토샵, 일러스트 및 기타 디자인툴을 다루는 시간을 다 합쳐도 비주얼 스튜디오를 사용하는 시간의 1/10 도 안될거에요.

요즘은 대부분의 개발을 c# 으로 진행하고 있습니다. 포토샵 스크립트를 이용해서 이미지 컨트롤은 할 수 있겠지만 좀더 복잡하고 다양한, 그리고 시스템 차원에서 뭔가를 하기에는 부족한 부분이 있습니다. 하지만 포토샵에는 레이어 컨트롤이나 layer effect 와 같은 놀라운 기능들이 있기 때문에 버리기는 아까운 부분이 있습니다.

그래서 저는 C# 을 이용하여 개발을 하되 포토샵을 이미지 자동화 편집 툴로 사용할때가 종종 있습니다. javascript 로 개발된 이미지 편집용 스크립트를 C# application 에서 포토샵으로 전달하여 자동화를 하는 것이지요.

 

??

 

이게 가능하냐고요? 

예전에 제가 엑셀 비주얼 베이직을 이용하여 엑셀과 포토샵이 연동되는 것을 소개해 드린적이 있는데요, 개념적으로는 크게 다르지 않습니다.

2019/10/01 - [DEV/VBA] - [VBA] 엑셀 VBA로 포토샵 연동이 가능할까?

 

개념적으로는 윈도우의 COM 오브젝트를 이용하는 것과 동일한데요. 연결해주는 방법에 약간 차이가 있고 C# 에서 포토샵 스크립트 작성이 쉽지 않으니 개발은 Extend Script Tool kit 으로 하고 실행만 C#이 하는 역할을 하는 겁니다.

이렇게 되면 C#이 다양한 파일 처리, 관리를 하는 동안 이미지 편집이 필요한 순간에 Photoshop 을 호출하여 이미지를 열고 자동화 편집 스크립트를 통해 이미지 편집을 진행하고 그 다음 나머지 파일 처리를 하는 방식 인 겁니다.

 

 

 

C# 에서 COM 오브젝트 선언하는 방법

Type ptsApp = Type.GetTypeFromProgID("Photoshop.Application");
dynamic psApp = Activator.CreateInstance(ptsApp);
psApp.doJavaScript("alert(\"ds\")"); // 여기에 수행되어야 할 포토샵 스크립트를 문자열로 작성하여 전달

dynamic 개체를 이용하여 선언이 가능하며 .NET4.0 이상의 플랫폼에서 지원합니다.

이렇게 했을 때 빌드를 하게 되면 아래와 같은 오류가 나타나는 경우가 있는데요. 

Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create' 멤버가 필요한 컴파일러가 없습니다

 

우측의 솔루션 탐색기 '참조' 부문에서 마우스 우클릭 후 '참조 추가' 로 참조할 개체를 추가해 주어야 하는데요.

Microsoft.CSharp 

을 추가해주시면 정상적으로 빌드가 진행이 됩니다.

 

막상 포토샵 오브젝트를 추가해 주었지만 실제 실행될 때 C# 에서 포토샵 코드를 작성하는 것이 여간 귀찮은 일이 아닙니다. 그런 경우 미리 javascript 로 필요한스크립트를 작성하여 준뒤 아래와 같이 실행시킬 수 있습니다. 이렇게 하면 실행시 미리 변수를 전달 할 수 있기 때문에 상당히 유연한 개발을 진행할 수 있게 됩니다.

dynamic[] args = new dynamic[2];
args[0] = "test string 1";
args[1] = "test string 2";

dynamic app = Activator.CreateInstance(Type.GetTypeFromProgID("Photoshop.Application"));

String scriptPath = @"D:\test\route_point_extractor_samples\script1.jsx";
string scriptStr = File.ReadAllText(scriptPath);
app.DoJavaScriptFile(scriptPath, args);

변수는 위에 보시는것 과 같이 dynamic 배열 개체를 생성한뒤 필요한 값을 입력해주고 스크립트와 함께 전달하는 방식을 사용하면 됩니다. 

 

 

 

 

포토샵 스크립트 실행 완료 후 반환(리턴) 값이 필요한 경우라면?

무언가 일을 시켰다면 피드백이 있어야 하겠지요? 만들어진 개체의 파일 명이든 무엇이든 간에 어떤 피드백을 받아야 하는 경우 아래와 같이 작성합니다.

 

Javascript (포토샵에서 실행되어야 할 스크립트)

var value1 = arguments[0]; // C# 으로 부터 전달 받는 인자 
var value2 = arguments[1]; // C# 으로 부터 전달 받는 인자

main(value1, value2)

function main (val1, val2)
{
	string myString = "";
    
    // 실제 필요한 계산, 동작을 작성한다.
    
    myString = val1 + "," + val2 ;
	
    return myString; // 이 값이 C# 으로 반환된다.
}

 

C#

dynamic[] args = new dynamic[2];
args[0] = "test string 1";
args[1] = "test string 2";

dynamic app = Activator.CreateInstance(Type.GetTypeFromProgID("Photoshop.Application"));

String scriptPath = @"D:\test\route_point_extractor_samples\script1.jsx";
string scriptStr = File.ReadAllText(scriptPath);

string myResult = app.DoJavaScriptFile(scriptPath, args);

// 결과값은 "test string 1,test string 2" 가 반환된다.

이렇게 해주면 myResult 라는 문자열 변수에 입력한 변수들을 합친 문자열이 반환이 되는것이죠.

어떠신가요? 어렵지 않죠?

 

 

 

 

 

이렇게 하면 C# 으로 빠르고 편리한 이미지 관리 프로그램을 만들어 포토샵으로 강력한 이미지 편집 기능을 함께 이용할 수 있는 기능을 개발 할 수 있습니다. 물론 ImageMagick 과 같은 강력한 이미지 편집 프로그램이 있긴 하지만 이미 만들어져 있는 PSD 파일 등의 레이어속성을 조회하거나 변경하고, 특정 레이어들을 이용하여 어떤 작업을 해야 한다면 ImageMagick 으로는 한계가 있습니다. 

직접 한번 코딩을 해보시면서 테스트 해보시길 바랍니다.

 

궁금하신 부분은 뎃글로 남겨 주시면 답변 드릴 수 있도록 하겠습니다.

이만 포스팅을  마칩니다.

뎃글, 공감 은 블로그 작성자에게 큰 힘이 된답니다. 
도움이 되었다 생각되시면 클릭!!  부탁드려요~

 

 

 

2020/04/03 - [DEV/c#] - ImageMagick 을 이용하여 이미지 컨트롤 해보기

 

ImageMagick 을 이용하여 이미지 컨트롤 해보기

포토샵 없이 이미지를 편집하는 방법이 없을까 고민하다가 알게된 라이브러리, 바로 ImageMagick 입니다. 먼저 포스트에서 ImageMagick 을 프로젝트에 가져오는 방법을 아래와 같이 소개해 드린적이 ��

diy-dev-design.tistory.com

2019/06/21 - [DEV/c#] - [C#] C# .NET 프로젝트에서 ImageMagick 이용하기

 

[C#] C# .NET 프로젝트에서 ImageMagick 이용하기

이미지 관련된 툴을 개발함에 있어 다양한 이미지 포멧을 만들고 변경하기 위한 코어를 개발하는 것은 말도 안되게 힘든 작업입니다. 저는 디자이너라는 타이틀을 가지고 있기 때문에 포토샵이

diy-dev-design.tistory.com

2020/01/03 - [DEV/c#] - [C#] C# APP 에서 엑셀 연동해보기

 

[C#] C# APP 에서 엑셀 연동해보기

엑셀에서 VBA 로 코드를 짜다 보면 막상 특정 엑셀 파일에만 VBA 가 적용이 가능하기 때문에 막상 다른 엑셀 파일에서 동일한 동작을 하려면 또 코드를 복사해 넣고 실행을 해야 하는 경우가 있습�

diy-dev-design.tistory.com

2019/06/10 - [DEV/Adobe Script] - [포토샵 스크립트] 강좌, 무작정 따라해보기

 

[포토샵 스크립트] 강좌, 무작정 따라해보기

어도비 포토샵은 자체 스크립트를 이용하여 다양한 기능을 수행할 수 있는 툴을 지원합니다. 언어는 자바 스크립트 이며 작성된 자바스크립트를 포토샵에서 실행하는 방법과 Extend Script Tool Kit �

diy-dev-design.tistory.com

2020/01/23 - [DEV/Adobe Script] - [포토샵스크립트] 레이어 컨텐츠 영역 선택하기

 

[포토샵스크립트] 레이어 컨텐츠 영역 선택하기

오늘 소개해 드릴 스크립트는 간단한 내용이지만 일반 적인 스크립트상으로 개발이 불가능하기에 경우에 따라는 아주 유용한 스크립트가 되겠습니다. 우리는 작업 중 상당한 빈도로 포토샵 레�

diy-dev-design.tistory.com

 

 

반응형
반응형

오늘 포스트에서는 지난 엑셀 자동화 툴의 뎃글로 문의 주신 헤더에 이미지를 VBA 를 이용하여 자동으로 삽입하는 내용을 소개해 드릴까 합니다.

 

"엑셀파일 수백개가있는데
파일의 시트마다 머리글에 이미지를 삽입해야합니다
이런것도 가능한가요?
아니면 만들어주신 프로그램에서 2행정도를 추가한뒤에 전체시트에 한번에 입력하는것도 가능한지요?"

출처: https://diy-dev-design.tistory.com/84?category=793981 [개발하는 디자이너의 DIY 일상]

네 당연히 가능하고 약간 시간이 걸리긴 했지만 유용할 듯 하여 VBA 로 작성하여 보았습니다.

나중에 시간내서 제가 만든 자동화 툴에도 구현 되도록 해보겠습니다.

 

오늘 내용은 어렵지 않은 내용이므로 차근차근 확인하시며 따라 오시면 될 것 같습니다.

 

 

먼저 다음의 객체로 VBA 에서 접근하여 설정을 진행하는 것을 기억해 두시기 바랍니다.

Worksheet.PageSetup.LeftHeaderPicture.Filename = "이미지 파일 경로"
Worksheet.PageSetup.LeftHeader = "헤더에 입력할 텍스트"

 

위와 같이 worksheet 내에 PageSetup 이라는 개체로 진입한 뒤 PageSetup 개체 내에 각종 헤더 위치에 따라 필요한 설정을 진행할 수 있습니다. 

Worksheet.PageSetup.붙일위치/타입

머릿말 및 꼬릿말의 위치 및 타입은 아래와 같은 대상을 정하여 설정을 진행할 수 있습니다. 

그림을 넣을 경우

LeftHeaderPicture  CenterHeaderPicture  RightHeaderPicture
본문
LeftFooterPicture  CenterFooterPicture  RightFooterPicture

 

글자를 넣을 경우

LeftHeader   CenterHeader  RightHeader
본문
LeftFooter   CenterFooter  RightFooter

 

그 중 한군데에 그림을 적용하는 코드 샘플을 보여드리면 아래와 같습니다.

Sub headerInsertTest()

    Dim aSht As Worksheet    
    Set aSht = ActiveSheet ' 현재 활성화된 시트'
    
    With aSht.PageSetup   ' 반복되는 문장으로 코드가 길어지지 않도록 with 사용'
        With .LeftHeaderPicture
            .Filename = "D:\myTestImage_25.png" ' 이미지 경로'
            .Height = 25    ' 이미지의 크기를 입력'
            .Width = 25     ' 이미지의 크기를 입력'
            .ColorType = msoPictureAutomatic
        End With        
        .LeftHeader = "&G" ' 헤더에 그림을 표시함'
        .Zoom = 100        
    End With

End Sub

 

역시 동일한 방식으로 글자를 넣게 되면 약간만 수정해 주면 됩니다.

Sub headerInsertTest()

    Dim aSht As Worksheet    
    Set aSht = ActiveSheet ' 현재 활성화된 시트'
    
    With aSht.PageSetup   ' 반복되는 문장으로 코드가 길어지지 않도록 with 사용'
        .LeftHeader = "This Header is writen by VBA"                    
    End With

End Sub

 

위의 방식으로 하면 VBA 를 이용하여 헤더에 그림이나 글씨를 적용하는 것이 가능합니다.

 

 

폴더 내 모든 엑셀 파일에 동일한 머릿글 이미지 적용하려면?!?!

먼저 다른 포스트에서 요청 주신바와 같이 특정 폴더에 엑셀 시트가 수백개가 있다고 하셨는데요. 해당 폴더 내 모든 엑셀 파일에 동일한 헤더 이미지를 적용한다고 하면 약간 코드가 복잡해 지겠지요.

아마 아래와 같은 과정이 필요할 것 같습니다.

  • 폴더를 지정하면 폴더내의 엑셀 파일의 리스트를 가져온다.
  • 가져온 엑셀 파일에 대하여 하나씩 열어서 다음의 과정을 처리한다
    • 엑셀에 있는 모든 시트에 대하여 각각 작업이 진행되도록 한다.
    • 이미지의 경로를 이용하여 헤더에 그림을 삽입한다.
  • 처리가 완료된 엑셀 파일은 저장한다.

 

어떤가요? 감이 오시나요?

제가 넣기 위하여 준비한 이미지는 아래와 같습니다.

 

테스트용 엑셀 파일은 아래와 같이 만들어 두었습니다. 하위 폴더가 있는 경우 하위 폴더의 엑셀 파일에도 적용이 되어야 하므로 하위 폴더도 만들어 준비를 했습니다. 

엑셀파일은 위와 같이 준비를 했습니다.

한번 작성된 코드를 보시죠.

Sub insertHeadertoAllXLSfiles()
    Dim fso As Object
    Dim fsoFolder As Object
    Dim rootPath As String
    Dim imagePath As String
    
    rootPath = "D:\test\files"
    imagePath = "D:\test\files\diy_dev_design_01.png"
    
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set fsoFolder = fso.GetFolder(rootPath)
    
    getDataRecursive fsoFolder, imagePath
End Sub

Sub getDataRecursive(ByVal baseFolder As Object, ByVal imgName As String)
    
    Dim fso As Object
    Dim tmpSubFolders As Object
    Dim tmpFiles As Object
    
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set tmpSubFolders = baseFolder.subFolders
    Set tmpFiles = baseFolder.Files
    
    For Each c In tmpFiles
        '여기서 각 엑셀에 실행해야 할 코드를 수행함'

        If LCase(Right(c.Name, 3)) = "xls" Or LCase(Right(c.Name, 4)) = "xlsx" Then ' xls, 또는 xlsx 인 경우'
            '엑셀 파일 열기'
            Dim aBook As Workbook
            Set aBook = Workbooks.Open(c)
            
            '모든시트에 헤더를 넣는 함수 실행'
            For Each tmpsht In aBook.Worksheets
                headerInsertTest tmpsht, imgName
            Next tmpsht
            
            ' 저장하고 닫기'
            aBook.Save
            aBook.Close
        End If
    Next c
    
    For Each d In tmpSubFolders
        
        Dim tmpSub As Object
        Set tmpSubs = fso.GetFolder(d)
        
        ' 하위폴더가 있는경우 하위폴더까지 계속 탐색함 (재귀함수)'
        getDataRecursive tmpSubs, imgName
    Next d
End Sub

Sub headerInsertTest(ByVal sht As Worksheet, ByVal imgPath As String)

    Dim wia         As Object       '이미지 크기 확인을 위한 개체'
    
    Set wia = CreateObject("WIA.ImageFile")
    wia.LoadFile (imgPath)
    
    With sht.PageSetup
        
        With .CenterHeaderPicture       ' 상단 중앙 머릿글에 이미지 넣기'
            .Filename = imgPath
            .Height = wia.Height        ' 이미지의 세로 크기를 입력'
            .Width = wia.Width          ' 이미지의 가로 크기를 입력'
            .ColorType = msoPictureAutomatic
        End With
        .CenterHeader = "&G" ' 헤더에 그림을 표시함'
        .Zoom = 100
    End With
End Sub



 

좀 길죠? 

여기에는 총 3개의 함수가 들어있습니다.

  1. 실제 코드를 실행 시키는 함수 
  2. 하위 폴더를 탐색하며 엑셀 파일을 만나면 열어서 시트별로 3번 함수를 실행
  3. 워크시트의 머릿글에 이미지를 넣는 함수

이런 순서로 들어있고요. 

1번 함수에서 원하시는 엑셀 파일이 들어있는 경로와 머릿글에 들어갈 이미지의 경로를 지정해 준 뒤 실행하면 됩니다. 제가 실행해 보니 실행 속도가 다소 느리기는 하지만 정상적으로 동작이 잘 되는 군요.

첫번째 함수에서 

    rootPath = "D:\test\xlss"
    imagePath = "D:\test\xlss\diy_header.png"

부분에 이미지의 경로와 엑셀 파일이 들어있는 경로를 바꾸어 주신뒤 동작 시키면 모든 엑셀파일(xls, xlsx)에 아래와 같이 헤더 위치에 원하시는 이미지가 붙게 됩니다. 

모든 엑셀파일 및 시트에 위와 같이 머릿글 자리에 이미지가 붙었다.

 

참고로 위 스크립트는 머릿글을 붙여야할 문서에 작성하는 것이 아닙니다. 별도 엑셀 파일에 작성한 뒤 동작을 시켜야만 지정한 폴더에 있는 엑셀 파일들을 열어서 자동화 작업이 가능하겠죠.

스크립트가 길어 직접 작성이 어려우실 수 있을 것 같아 작성된 엑셀 파일을 첨부합니다.

insertHeader_all.xls
0.04MB

 

해보시고 어려운 부분이 있으면 뎃글 부탁드립니다.

뎃글공감은 블로그 작성자에게 큰 힘이 됩니다. 

 

감사합니다.

 

2019/06/01 - [DEV/VBA] - 엑셀 VBA 시작하기

 

엑셀 VBA 시작하기

마이크로 소프트 엑셀은 수많은 기능과 자동화된 연산 처리, 편리한 템플릿 가공, 다양한 그래프 드을 이용한 데이터의 시각화 외에도 아주 많은 유용한 기능을 제공하여 사무 업무의 표준 프로그램이 되었다. 나..

diy-dev-design.tistory.com

2019/08/12 - [DEV/VBA] - [VBA] 문자열 가지고 놀기

 

[VBA] 문자열 가지고 놀기

안녕하세요. 이번 글에서는 VBA 에서 문자열을 가지고 무엇인가를 하는 것을 알아보겠습니다. 프로그래밍을 하다 보면 조건을 가지고 어떤 액션을 해야 하는 결우가 무척 많은데요. 그중에 대표적인 것의 하나가..

diy-dev-design.tistory.com

2019/12/06 - [DEV/VBA] - [vba] 하위폴더 내의 모든 파일 정보 가져오기

 

[vba] 하위폴더 내의 모든 파일 정보 가져오기

일을 하다 보면 가끔 업무를 진행하던 경로 하위에 있는 데이들의 리스트를 만들어야 하는 경우가 있습니다. 하나의 폴더라면 어떻게 해보겠는데 그 폴더가 하위 뎁스가 연속해서 있고 저장되어 있는 파일이 불특..

diy-dev-design.tistory.com

2020/01/08 - [DEV/c#] - Excel Automate, 엑셀 자동화 프로그램

 

Excel Automate, 엑셀 자동화 프로그램

필자의 블로그 명칭을 보고 이미 알고 계신분이 있을지 모르겠지만 사실 저는 디자이너 입니다. 하지만 블로그에 맨 개발 관련 된 이야기만 적고 있지요. 음... 그런데 실제로 회사에서도 저는 대부분의 시간을 코..

diy-dev-design.tistory.com

 

반응형
반응형

이번 포스트에서는 vba 를 이용하여 셀의 속성을 조정하는 방법을 설명 드리겠습니다.

엑셀은 단순한 표의 형식을 취하고 있지만 셀의 간격이나 테두리 등의 속성을 자유롭게 조정할 수 있어 다양한 서식에 사용하기 적합한 훌륭한 워드 프로세서 입니다.

데이터의 타입이나 종류에 따라 현재 엑셀 시트의 표를 자동으로 설정해주는 것이 필요한 경우가 있습니다. 

예를 들면 csv 로 내려받은 단순한 정보들을 시트에 올리고 필요한 정보와 필요하지 않은 정보의 열을 구분하여 표시해 준다던가 아예 불필요한 열이나 행은 숨김 처리를 할 수도 있고요. 주요 행의 테두리 정보를 구분하여 보여주어야 하는 경우도 있겠습니다. 또한 서실을 vba로 만들어 놓으면 어떤 다른 인원에 의해 조정된 폼을 원래 지정한 형식으로 완벽하고 빠르게 설정하는 것도 가능할 것입니다.

이번 포스트에서는 아래의 내용을 다루려고 합니다.

  • 행의 높이를 조절하는 방법
  • 열의 넓이를 조정하는 방법
  • 행 또는 열을 숨기거나 숨김 취소 하는 방법
  • 테두리 선을 지정하는 방법
  • 테두리 선의 모양을 지정하는 방법

 

 

엑셀 VBA 를 이용하여 행(row) 높이를 지정하는 방법

Sub changeCellStyle()

    Dim mySht As Worksheet
    Dim myCell As Range
    
    Set mySht = ActiveSheet
    Set myCell = mySht.Range("B2") ' 원하는 행의 셀을 지정
    
    
    ' 지정한 셀(행)의 높이를 32로 설정한다.
    myCell.RowHeight = 32
    
    '픽셀 기준으로 입력하고 싶다면 0.75 를 곱하세요.
    myCell.RowHeight = 32 * 0.75


End Sub

 

행의 높이를 Pixel 을 기준으로 지정해야 하는 경우 pixel size * 0.75 를 해주면 됩니다.

예를 들면 이미지를 붙여넣는 등의 작업을 할때 이미지의 크기가 150 이라면 150 * 0.75 의 값을 셀의 크기에 지정을 하면 됩니다. 아래와 같이 말이죠.

 

 

엑셀 VBA 를 이용하여 열(column)의 넓이를 지정하는 방법???

여기서 셀의 폭을 지정하는 방법은 갑자기 이야기가 달라집니다. 저는 그래픽 디자이너 이므로 당연히 픽셀 기준으로 셀의 넓이를 정해주고 싶거든요. 여기서 미치고 팔딱 뛰는 상황이 생겨나게 됩니다.

정말 저는 마이크로소프트라는 회사를 이해할 수가 없습니다. 

이부분은 아래 포스팅을 참고해 주세요. 작성하면서도 맨붕이네요.

2020/02/05 - [DEV/VBA] - [VBA] 셀의 넓이를 픽셀로 지정하기 (set column width by pixels)

 

[VBA] 셀의 넓이를 픽셀로 지정하기 (set column width by pixels)

정말 지긋지긋한 마이크로 소프트 입니다. 저는 VBA 를 하면서 이번 포스트 내용만큼 어의가 없는 경우는 없었던거 같습니다. 왜 때문이냐고요? 바로 아주 단순한 셀 넓이를 VBA 를 이용하여 지정하는 방법 때문입..

diy-dev-design.tistory.com

 

 

행 또는 열을 숨기거나 숨김 취소하는 방법

Sub changeCellStyle()
    
    Dim mySht As Worksheet
    Dim myCell As Range
    
    Set mySht = ActiveSheet
    Set myCell = mySht.Range("B2") ' 원하는 행의 셀을 지정'
    
	
    ' 선택한 셀이 있는 모든 행을 숨김처리할 때'
    myCell.EntireRow.Hidden = True
    
    ' 선택한 셀이 있는 모든 행을 숨김취소 할때'
    myCell.EntireRow.Hidden = False

End Sub

 

숨기거나 해제 하는 것은 간단하죠?

entireRow 라는 개체를 이용하여 전체 행에 대한 지정을 한뒤 hidden 이라는 프라퍼티로 숨기거나 숨기지 않도록 설정 할 수 있습니다.

열 (column) 을 숨기거나 해제 하는 것도 완전히 동일합니다.

아래와 같이 entireRow 대신 entireColumn 으로 사용하면 됩니다.

    myCell.EntireColumn.Hidden = True
    
    myCell.EntireColumn.Hidden = False

 

 

테두리의 선을 지정 (해제) 하는 방법

선택한 셀 또는 행의 모든 테두리를 없애는 방법

Sub changeCellStyle()
    
    Dim mySht As Worksheet
    Dim myCell As Range
    
    Set mySht = ActiveSheet
    Set myCell = mySht.Range("B2") ' 원하는 행의 셀을 지정
    
    ' 선택한 셀의 테두리를 없애는 방법 '
    myCell.Borders.LineStyle = xlNone
    
    '선택한 행전체의 테두리를 없애는 방법
    myCell.EntireRow.Borders.LineStyle = xlNone
    
    
End Sub

 

선택한 셀의 테두리를 지정하는 방법

Sub changeCellStyle()
    
    Dim mySht As Worksheet
    Dim myCell As Range
    
    Set mySht = ActiveSheet
    Set myCell = mySht.Range("B2") ' 원하는 행의 셀을 지정
    
    '선택한 행전체의 테두리를 없애는 방법
    myCell.EntireRow.Borders.LineStyle = xlNone
    
    
    
    ' 선택한 셀의 왼쪽 테두리만 보통의 실선으로 보여주는 방법 '
    myCell.Borders(xlEdgeLeft).LineStyle = xlContinuous
    
    ' 선택한 셀의 오른쪽 테두리만 보통의 실선으로 보여주는 방법 '
    myCell.Borders(xlEdgeRight).LineStyle = xlContinuous
    
    ' 선택한 셀의 위쪽 테두리만 보통의 실선으로 보여주는 방법 '
    myCell.Borders(xlEdgeTop).LineStyle = xlContinuous
    
    ' 선택한 셀의 아래쪽 테두리만 보통의 실선으로 보여주는 방법 '
    myCell.Borders(xlEdgeBottom).LineStyle = xlContinuous
    
   ' 선택한 셀의 테두리를 보통의 실선으로 보여주는 방법 '
    myCell.Borders.LineStyle = xlContinuous
    
End Sub

제가 방향별로 지정하는 방법을 적어 두었으니 필요한 내용을 추려다가 사용하시면 됩니다.

 

 

테두리의 선의 속성을 변경하는 하는 방법

만약 아래쪽 선만 두껍게 지정하고 싶다면?

' 선택한 셀의 아래쪽 테두리만 두꺼운 실선으로 보여주는 방법 '
    myCell.Borders(xlEdgeBottom).Weight = xlThick
    myCell.Borders(xlEdgeBottom).LineStyle = xlContinuous

요렇게 해주시면 됩니다.

참고로 라인 스타일을 xlContinuous 말고 점선이나 다양한 스타일로 적용하는 것도 가능합니다.

  • xlContinuous 연속선입니다.
  • xlDash 파선입니다.
  • xlDashDot 교대로 연결된 파선과 점선입니다.
  • xlDashDotDot 파선과 두 개의 점선입니다.
  • xlDot 점선입니다.
  • xlDouble 이중선입니다.
  • xlLineStyleNone 선이 없습니다.
  • xlSlantDashDot 기울어진 파선입니다.

 

위에 소개해드린 여러가지 속성들을 조합하면 하나의 완성된 폼을 자동으로 생성하는 것이 가능합니다.

한번 짜 놓을 때는 귀찮을 수 있지만 수도없이 계속해서 폼을 맞추어주어야 하는 어떤 상황이라면 자동화 개발을 하는것이 정신건강이나 육체건강에 좋겠죠.

 

셀의 색상을 채워넣는 방법은 이전 포스트를 참고해 주세요.

2019/08/30 - [DEV/VBA] - [VBA]RGB 색상 값이 들어있는 셀에 셀 색상 지정하기

 

[VBA]RGB 색상 값이 들어있는 셀에 셀 색상 지정하기

이번 강좌에서는 셀에 입력되어 있는 컬러 값을 이용하여 셀의 색상을 지정하는 방법을 알아보도록 하겠습니다. 디자이너 분들이라면 언제가 한번쯤은 엑셀 시트에 자신이 정의한 컬러 값을 정

diy-dev-design.tistory.com

이렇게 간단하게 vba 를 이용하여 셀의 모양을 조정하는 것이 가능합니다.

어렵게 생각치 마시고 위에 설명드릴 방법을 다양하게 응용하여 자신에게 맞는 자동화 툴을 구성하시기 바랍니다.

2020/09/04 - [DEV/VBA] - [vba] 엑셀 이미지 리스트로 일괄 다운로드 받기

 

[vba] 엑셀 이미지 리스트로 일괄 다운로드 받기

안녕하세요. 오늘은 엑셀 리스트로 가지고 있는 웹 이미지 경로를 이용하여 일괄 다운로드 하는 방법을 소개해 드릴까 합니다. 인터넷에 찾아보면 이런저런 다운로더들이 있는데요. 입맛에 맞

diy-dev-design.tistory.com

2020/06/22 - [DEV/VBA] - [vba] 초등학교 연산 자동 문제집 - 곱셈 추가

 

[vba] 초등학교 연산 자동 문제집 - 곱셈 추가

안녕하세요 주인장입니다. 코로나로 인한 개학연기로 아이들이 집안에서만 딩굴딩굴 거려 답답하신가요? 아이들에게 신나는 연산 문제를 내 주는건 어떨까요? ?? 는 아니고 VBA 로 제가 그동안

diy-dev-design.tistory.com

2019/12/06 - [DEV/VBA] - [vba] 하위폴더 내의 모든 파일 정보 가져오기

 

[vba] 하위폴더 내의 모든 파일 정보 가져오기

일을 하다 보면 가끔 업무를 진행하던 경로 하위에 있는 데이들의 리스트를 만들어야 하는 경우가 있습니다. 하나의 폴더라면 어떻게 해보겠는데 그 폴더가 하위 뎁스가 연속해서 있고 저장되

diy-dev-design.tistory.com

감사합니다.

반응형
반응형

포토샵의 가장 중요한 핵심 기능중의 하나가 레이어라는 구조일 것입니다. 이미지를 계층적으로 쌓아 올리고 각각의 이미지간 효과를 적용하거나 투명도등을 조절하여 완성된 하나의 이미지를 만들어 내는 것이죠. 

오늘 강좌에서는 포토샵에 레이어를 생성하고, 이름을 붙이거나, 위치를 이동하는 등의 레이어를 관리하는 기능을 소개해 드릴까 합니다.

오늘 강좌에서 다룰 내용은 아래와 같습니다.

  • 이미 있는 레이어를 복제하기, 위치 조정하기, 투명도 조정하기
  • 이미 있는 레이어의 순서 변경하기(이동하기)

 

먼저 포토샵 스크립트 작성을 위하여 extend script toolkit 을 실행합니다.

그리고 당연히 포토샵 어플리케이션을 열어야 겠죠. 

오늘은 제가 좋아하는 traxxas 로고를 이용하여 코드 연습을 해보겠습니다.

이 로고 이미지를 이용하여 wallpaper 를 한번 만들어 보겠습니다. 손으로 하자면 아주 귀찮을 그림을 만들어 보겠습니다.

//로고파일의 위치를 설정한다
traxxasLogo_250_File = "D:/01_works/용/DIY_HARD_RC/traxxas_LOGO_125x25.png"
var logoFile = new File(traxxasLogo_250_File);

// 포토샵에 새로운 도큐먼트를 생성한다. 크기는 1920 1080
var wallPaperPsd = app.documents.add(1920,1080,72)

// 검정색 컬러를 정의한다
var newColor = new SolidColor();
newColor.rgb.red = 0;
newColor.rgb.green = 0;
newColor.rgb.blue = 10;

// 배경색상을 검정색으로 fill 한다
wallPaperPsd.selection.selectAll();
wallPaperPsd.selection.fill(newColor);
wallPaperPsd.selection.deselect();

// 로고 이미지를 열어서 포토샵으로 복사한다. 
var logoImg = app.open(logoFile);
var newLogoLyr = logoImg.layers[0].duplicate(wallPaperPsd.layers[0], ElementPlacement.PLACEBEFORE);


app.activeDocument = logoImg;
// 로고 파일은 닫는다.
logoImg.close();

app.activeDocument = wallPaperPsd;
//복사한 레이어의 이름을 변경한다.
newLogoLyr.name = "logo_1_1";

오늘 강좌의 핵심이되는 부분은 요 부분입니다.

var newLogoLyr = logoImg.layers[0].duplicate(wallPaperPsd.layers[0], ElementPlacement.PLACEBEFORE);

logoImg 파일의 0번째 레이어를 복사하는 명령어죠. duplicate 뒤에 오는 값들은 어디에 복사할 것인지를 지정합니다. 첫번째 인자는 복제될 대상을 지정합니다. 이번 예제에서는 다른 PSD 파일 (배경화면용 이미지)의 0번째 레이어가 대상이 되었습니다. 그뒤에 오는 인자는 지정한 대상과의 관계인데요. 이번 예제에서 사용한 것은 "대상 예제의 바로 전에 위치시켜라" 라는 명령입니다.

ElementPlacement 라는 개체의 하위 옵션으로 몇가지 옵션을 지정할 수 있는데요. PS 버전이 올라가면서 옵션이 많이 다양해 졌습니다. 필요한 위치에 맞추어 넣으시면 되겠습니다.

ElementPlacement.PLACEAFTER         //cs6 - 대상개체 아래에
ElementPlacement.PLACEATEND         //cs6 - 대상개체의 제일 마지막에
ElementPlacement.PLACEBEFORE        //cs6 - 대상개체의 바로 위에
ElementPlacement.INSIDE             //cc - 대상이 폴더인 경우 폴더 안에
ElementPlacement.PLACEATBEGINNING   //cc - 대상 개체의 제일 처음에

 

어쨌든 요기까지 스크립트를 실행해보면 아래와 같은 그림이 만들어 집니다.

스크립트를 이용해 포토샵 psd 파일에 다른 파일의 이미지를 레이어로 복사한 상태

화면에 로고 이미지가 붙었죠?

요 이미지를 화면에 쫙 깔아 보겠습니다. 손으로 하려면 매우 귀찮은 작업이죠.

위에 작성한 코드를 포함하여 추가한 코드는 아래와 같습니다.

//로고파일의 위치를 설정한다
traxxasLogo_250_File = "D:/01_works/용/DIY_HARD_RC/traxxas_LOGO_125x25.png"
var logoFile = new File(traxxasLogo_250_File);

// 포토샵에 새로운 도큐먼트를 생성한다. 크기는 1920 1080
var wallPaperPsd = app.documents.add(1920,1080,72)

// 검정색 컬러를 정의한다
var newColor = new SolidColor();
newColor.rgb.red = 0;
newColor.rgb.green = 0;
newColor.rgb.blue = 10;

// 배경색상을 검정색으로 fill 한다
wallPaperPsd.selection.selectAll();
wallPaperPsd.selection.fill(newColor);
wallPaperPsd.selection.deselect();

// 로고 이미지를 열어서 포토샵으로 복사한다. 
var logoImg = app.open(logoFile);
var newLogoLyr = logoImg.layers[0].duplicate(wallPaperPsd.layers[0], ElementPlacement.PLACEBEFORE);

app.activeDocument = logoImg;
// 로고 파일은 닫는다.
logoImg.close();

app.activeDocument = wallPaperPsd;
//복사한 레이어의 이름을 변경한다.
newLogoLyr.name = "logo_def";

// 이제 화면에 로고 이미지를 가득 채워 넣자

//각 로고 간의 간격을 설정해주자
var gapWidth = 100;
var gapHeight = 100;

// 로고 이미지의 영역을 구한다. bounds 는 레이어내의 이미지 영역 정보를 가져온다
var logoBounds = newLogoLyr.bounds; //영역 정보는 [left, top, right, bottom] 순으로 들어온다.
var logoWidth = logoBounds[2] - logoBounds[0]; // 레이어의 가로 크기
var logoHeight = logoBounds[3] - logoBounds[1]; // 레이어의 세로크기

// 화면 영역내에 몇개의 로고가 들어갈 수 있는지 계산한다.
var numWidth = wallPaperPsd.width / (logoWidth + gapWidth)
var numHeight = wallPaperPsd.height / (logoHeight + gapHeight)

// 가로, 세로로 로고를 복사하여 붙여 넣는다.
for (var yy = 0; yy < numHeight ; yy++) // 세로 개수만큼 반복
{
    for (var xx = 0; xx < numWidth; xx++) // 가로 개수만큼 반복
    {
        var tmpLyr = newLogoLyr.duplicate(newLogoLyr, ElementPlacement.PLACEBEFORE);
        tmpLyr.translate(xx * (logoWidth + gapWidth), yy * (logoHeight + gapHeight))
        tmpLyr.name = "logo_" + xx + "_" + yy ;
    }    
}

 

로고 이미지를 가로세로로 100px 씩 간격을 띄고 화면에 꽉 채우는 스크립트가 되었습니다.

이렇게하면 아래와 같은 그림이 만들어 집니다.

traxxas 로고로 꽉 채워진 이미지가 만들어 졌다.

네 그런데 약간 아쉬운건 로고가 약간 치우쳐져서 배치가 되었네요.

그리고 약간 단조로워 보이는 듯 합니다. 그리고 바탕화면에 사용할 그림인데 너무 강렬한 감이 있습니다.

자 코드를 좀 수정해 보겠습니다. 이제 코드가 제법 길어지므로 집중해서 보셔야 합니다.

var currentUnitValue = app.preferences.rulerUnits

// 포토샵 유닛을 픽셀로 변경
app.preferences.rulerUnits = Units.PIXELS;



//로고파일의 위치를 설정한다
traxxasLogo_250_File = "D:/01_works/용/DIY_HARD_RC/traxxas_LOGO_125x25.png"
var logoFile = new File(traxxasLogo_250_File);

// 포토샵에 새로운 도큐먼트를 생성한다. 크기는 1920 1080
var wallPaperPsd = app.documents.add(1920,1080,72)

// 검정색 컬러를 정의한다
var newColor = new SolidColor();
newColor.rgb.red = 0;
newColor.rgb.green = 0;
newColor.rgb.blue = 10;

// 배경색상을 검정색으로 fill 한다
wallPaperPsd.selection.selectAll();
wallPaperPsd.selection.fill(newColor);
wallPaperPsd.selection.deselect();

// 로고 이미지를 열어서 배경화면용 포토샵으로 복사한다. 
var logoImg = app.open(logoFile);
var newLogoLyr = logoImg.layers[0].duplicate(wallPaperPsd.layers[0], ElementPlacement.PLACEBEFORE);

app.activeDocument = logoImg;
// 로고 파일은 닫는다.
logoImg.close();

app.activeDocument = wallPaperPsd;
//복사한 레이어의 이름을 변경한다.
newLogoLyr.name = "logo_def";

// 이제 화면에 로고 이미지를 가득 채워 넣자

//각 로고 간의 간격을 설정해주자. 숫자가 작을 수록 촘촘해지고 클수록 듬성듬성 붙는다.
// 포토샵 스크립트 실행 속도가 느리므로 너무 작은 숫자는 넣지 말자.
var gapWidth = 100;
var gapHeight = 100;

// 로고 이미지의 영역을 구한다. bounds 는 레이어내의 이미지 영역 정보를 가져온다
var logoBounds = newLogoLyr.bounds; //영역 정보는 [left, top, right, bottom] 순으로 들어온다.
var logoWidth = logoBounds[2] - logoBounds[0]; // 레이어의 가로 크기
var logoHeight = logoBounds[3] - logoBounds[1]; // 레이어의 세로크기

// 화면 영역내에 몇개의 로고가 들어갈 수 있는지 계산한다.
var numWidth = Math.floor(wallPaperPsd.width / (logoWidth + gapWidth));
var numHeight = Math.floor(wallPaperPsd.height / (logoHeight + gapHeight));

// 가로, 세로로 가득 찼을때 남는 공간을 계산하여 offset 을 만들어 주자
var offsetX = Math.floor(wallPaperPsd.width - (numWidth * (logoWidth + gapWidth) - gapWidth));
offsetX = offsetX * 0.5;

var offsetY = Math.floor(wallPaperPsd.height - (numHeight * (logoHeight + gapHeight) - gapHeight));
offsetY = offsetY * 0.5;

// 투명도를 조절하기 위한 값. 여기서 설정한 값에 의해 투명도가 자동으로 조절되어 아래로 내려갈 수록 투명해 진다. 
var maxOpacity = 50;
var minOpacity = 15;

// 각 단계별 투명도 변이 값을 계산하자.
var opacityStep = (maxOpacity - minOpacity) / numHeight

// 가로, 세로로 로고를 복사하여 붙여 넣는다.
for (var yy = 0; yy < numHeight ; yy++) // 세로 개수만큼 반복
{
    for (var xx = 0; xx < numWidth; xx++) // 가로 개수만큼 반복
    {
        var tmpLyr = newLogoLyr.duplicate(newLogoLyr, ElementPlacement.PLACEBEFORE);
        wallPaperPsd.activeLayer = tmpLyr; // 처음 레이어가 계속 활성화 되는 것을 막기 위하여 활성화된 레이어를 현재 복사된 레이어로 지정
        tmpLyr.translate(-logoBounds[0], -logoBounds[1]); // 복사된 레이어를 0,0 으로 이동시킴
        tmpLyr.translate((xx * (logoWidth + gapWidth)) + offsetX, (yy * (logoHeight + gapHeight)) + offsetY) // 지정된 위치로 이동
        tmpLyr.name = "logo_" + xx + "_" + yy ;
        
        tmpLyr.opacity = maxOpacity - (yy * opacityStep); // 아래로 갈 수록 투명하게
        // tmpLyr.opacity = minOpacity + (yy * opacityStep); // 위로 갈수록 투명하게 하려면 주석을 풀어 줍니다.
    }    
}
//처음에 복사한 레이어는 필요 없으니 숨겨줍니다.
newLogoLyr.visible = false;

//유닛을 스크립트 실행 이전상태로 복구함
app.preferences.rulerUnits = currentUnitValue;




 

이제 스크립트는 아래로 복제해 나가면서 점차 투명도를 주어 서서히 보이지 않도록 하고 있습니다. 이 투명도로 사용자가 설정한 최대값과 최소값을 이용하여 점차 변화하도록 하고 있습니다. 

간격을 조정하여 촘촘하게 배치하거나 듬성듬성 배치하더라도 의도한데로 서서히 변화가 적용될 것 입니다.

적용된 화면을 다시 볼까요?

화면에 격자형으로 배치되지만 아래로 갈수록 투명도가 높은 상태가 되었다.

 

이렇게 하면 스크립트의 일부만 수정하여 다양한 화면을 만드는 것이 가능합니다.

반복하여 붙여넣을 파일의 경로, 반복 간격, 투명화 정도를 설정하는 것으로 다른 이미지에도 동일하게 적용이 가능합니다. 본 예제에서는 1920 1080 사이즈의 이미지로 제작하였지만 다른 사이즈를 입력하여도 아마 정확하게 동작할 것입니다. 

여기에 마지막으로 화면 가운데에 좀 더 커다란 로고를 넣는다면 어떻게 해야 할까요?

직접 한번 도전해 보시면 어떨까요?

[wallpaper] traxxas logo 1920 x 1080
[wallpaper] traxxas UDR 1920 x 1080

 

감사합니다. 그럼 이만~

 

레이어 위치 이동하는 스크립트는 여기에도 있었죠? ^^

https://diy-dev-design.tistory.com/46

 

[포토샵스크립트] 현재 레이어 이미지를 중앙으로 이동시키기

안녕하세요. 오늘은 선택된 레이어를 화면의 중앙으로 이동시키는 포토샵 스크립트를 작성해 보겠습니다. 와이프가 한동안 쇼핑몰 상세페이지 디자인 작업을 재택근무 형식으로 했었는데요. 옆에서 작업하는 것을..

diy-dev-design.tistory.com

 

반응형
반응형

이번 강좌에서는 좀 재미있는 코드를 소개해 드릴까 합니다.

바로 엑셀 VBA 를 이용하여 포토샵 자동화 하기! 

오~~ 이게 되냐구요? 네 ! 됩니다.

사실 스크립트나 자동화 작업을 통하여 어떤 일을 할 것인가를 찾아내는 것이 실제 코드를 작성하는 것보다 훨씬 어려운 일이랍니다. 막상 어느 정도 개발을 할 수 있게 되면 생각보다 많은 것을 할 수 있다는 것을 알게되죠.

자 오늘은 그런 의미에서 새로운 문을 한번 열어 보겠습니다.

포토샵은 크게 세가지 스크립트 언어를 지원합니다. 바로 JavaScript, Apple Script (맥에서 만 가능), Visual Basic (윈도우에서만 가능) 요렇게 세가지 입니다. 엑셀은 기본적으로 비주얼 베이직 에디터를 포함하고 있기 때문에 포토샵 스크립트인 비주얼 베이직 버전의 개발이 가능한 것이죠. 

비주얼 베이직 버전의 포토샵 스크립트도 많은 기능을 지원합니다. 자바스크립트 버전과 거의 동일합니다. 오히려 코딩 관점에서는 더 간단하기도 합니다.

 재미없는 이야기는 집어 치우고 바로 코드로 실습을 해보시죠.

엑셀을 켜고 Alt + F11을 눌러 비주얼 베이직 에디터 창을 열어줍니다. 그리고 Sheet1 페이지를 더블클릭해서 코딩을 위한 창을 하나 만든다음 아래의 내용을 입력해주세요.

Sub usingPhotoshop()

    Dim psApp As Object
    
    Set psApp = CreateObject("Photoshop.Application")
    
    psApp.Documents.Add

End Sub

물론 컴퓨터에 당연히 포토샵이 설치가 된 상태여야 겠죠?

F5 키를 눌러보면 쨔짠~ 아래와 같이 포토샵이 자동으로 켜지고 곧이어 새창이 하나 만들어 집니다.

엑셀 VBA 로 실행시킨 포토샵과 엑셀에서 만든 포토샵 다큐먼트

신기 하신가요?

제가 제 블로그에서 포토샵 스크립트도 틈틈히 올리고 있는데요. 해당 카테고리에는 대부분 자바스크립트 기준의 스크립트를 올리고 있습니다. 사실 해당 카테고리에 올린 모든 내용은 VBA 로도 작성이 가능하답니다.

코드를 조금더 올려볼까요?

Sub usingPhotoshop()

    Dim psApp As Object
    Dim psdDoc
    
    Set psApp = CreateObject("Photoshop.Application")
    
    psApp.Documents.Add
    
    Cells(1, 1).Value = "Document Name"
    Cells(1, 2).Value = "가로"
    Cells(1, 3).Value = "세로"
    
    Set psdDoc = psApp.activeDocument
    
    Cells(2, 1).Value = psdDoc.Name
    Cells(2, 2).Value = psdDoc.Width
    Cells(2, 3).Value = psdDoc.Height
        
End Sub

 조금 길어졌지만 뭐 한줄 한줄 읽어 보면 어려울 것이 없습니다. 처음 짜시려면 막막하시겠지만 막상 짜놓은 코드를 읽어보면 간단합니다. 자꾸 이렇게 만들어진 코드를 보는 것이 중요합니다. 자꾸 보면서 따라 코딩을 하다보면 눈에 익게 되고 그 다음엔 손에 익게되죠. 일단 익숙해 지면 쉽게 자신만의 코드 작성이 되기 시작합니다.

내용을 보면 이렇습니다.

  • 포토샵 어플리케이션이 될 변수와 PSD 다큐먼트를 지정할 변수를 선언해준다.
  • photoshop.application 이라는 오브젝트를 생성한다 --> 이때 포토샵이 실행됨
  • 실행된 포토샵에 새로운 다큐먼트를 추가한다.
  • 엑셀 시트의 멘위 세줄에 각 항목의 이름을 기입한다.
  • 현재 포토샵에서 활성화된 창을 변수에 담아준다.
  • 활성화된 창의 이름과 가로 길이, 세로 길이를 두번째 줄에 차례로 입력해준다.

요런식이 되겠습니다. 간단하죠?

포토샵의 정보를 이용하여 엑셀에 값을 채운 모습

자 그럼 반대로 엑셀에서 지정한 이름과 크기로 포토샵 도큐먼트를 세팅해 볼까요? 아마 실제로는 이게 더 유용하겠죠?

제 블로그 이름과 별도의 크기를 지정해 보겠습니다.

코드는 이렇게 작성해 보겠습니다.

  • 포토샵으로 새로운 다큐먼트를 만들어 준다
  • 가로세로 크기를 지정한 크기로 Resize 한다
  • 지정한 이름의 PSD 파일로 저장한다.

간단하죠? 코드를 볼까요? 아까랑 별반 차이가 없습니다.

Sub usingPhotoshop()

    Dim psApp As Object
    Dim psdDoc
    
    Set psApp = CreateObject("Photoshop.Application")
    
    psApp.Documents.Add
    
    Cells(1, 1).Value = "Document Name"
    Cells(1, 2).Value = "가로"
    Cells(1, 3).Value = "세로"
    
    Set psdDoc = psApp.activeDocument
    
    psdDoc.ResizeImage CInt(Cells(2, 2).Value), CInt(Cells(2, 3).Value), 72, 4
      
    psdDoc.SaveAs ("d:\" & Cells(2, 1).Value & ".psd")
    
End Sub

윗쪽까지는 다 동일하고요. 아래 두줄이 다릅니다.

바로 ResizeImage 라는 명령이 들어갔고요. 그 아래에는 다큐먼트의 이름이 되는 PSD 파일을 저장해주는 코드 입니다.

VBA 에서는 문자열을 합칠때 + 기호가 아닌 & 를 사용하는 것 알고 계시죠??

요렇게 하면 아래와 같이 만들어 진답니다. 사이즈 확인을 위하여 이미지 리사이즈 창을 통해 정보를 띄워 놓았습니다.

엑셀에서 작성한 데로 DIY-DEV-DESIGN 이라는 이름의 다큐먼트를 만들었다. 사이즈도 엑셀에서 지정한 크기로 설정된 상태.

사실 이런 간단한 코드도 처음 작성하는 분에게는 막막하기만 합니다.

VBA 는 익숙치 JavaScript 나 C, C++, Java 와는 다른 좀 특이한 문장 형태를 갖고 있기 때문에 더 어렵게 느껴질 수 있습니다. 하지만 VBA 의 장점이라 할 수 있는 실시간 오류 리포팅 기능이 있지 않겠습니까? 코드 작성 중 잘못된 문법은 허용이 되지 않죠. 바로 경고창으로 알려 줍니다. 경고 창이 뜨지 않도록 잘 맞춰주면 코드가 완성이 됩니다.

그리고 ResizeImaze 뒤에 Cells(2,1).value 는 알겠는데 그 뒤에 오는 72 와 4 라는 숫자는 뭘까요?? 각각의 값은 위 이미지에서 볼 수있는데요. 바로 리사이즈시 사용하는 Resolution 정보와 아래 resample 옵션인 Bicubic 에 해당하는 옵션입니다. 

이런건 어떻게 알아내냐구요? 바로 레퍼런스죠. 

레퍼런스에서 필요한 개체를 찾는다. 클릭하면 해당 개체로 이동됨

먼저 다큐먼트 개체로 진입합니다. 그다음 리사이즈를 해야 하니 resize 라는 용어 근처를 찾아봐야겠죠? 

스크롤해서 내려가다 보면 ResizeImage 라는 명령을 찾을 수 있다.

자 ResizeImage 라는 명령을 찾았습니다. 바로 위에는 ResizeCanvas 라는 명령도 보이네요. 어쨌든 ResizeImage 아래에 어떤 옵션들을 적어야 하는지 설명이 되어 있습니다.

  • ResizeImage
    • Width (숫자, 더블형 - 소수)
    • Height (숫자, 더블형 - 소수)
    • Resoluton (숫자, 더블형 - 소수)
    • ResampleMethode (PsResampleMethod)

위에 3개의 옵션은 아시겠죠? 입력해야 하는 데이터의 자료형 입니다. 소수라고 정의 되어 있으니 12.5 이런 값으로 입력이 가능합니다. 그런데 맨 아래는 뭔지 잘 모르시겠죠? 파란색으로 링크 표시가 있으니 클릭해서 들어가 보겠습니다.

resample 방법으로 이동된 모습, 숫자별로 숫자가 어떤 옵션이 무엇인지 설명이 되어 있다.

이미지를 인터폴레이션 즉, 보간하기 위한방법이라고 나와있군요. 각 숫자별로 보간 방법이 나와 있구요. 일반적으로 포토샵에서 기본으로 설정되어 있는 옵션은 Bicubic 이며 4번에 해당됩니다. 제가 사용한 코드에 4 로 입력하였으니 Bicubic 으로 보간이 된 셈입니다. 

막간을 이용하여 간단하에 각 옵션을 설명 드리자면, (이번 기회에 알아두셔도 좋겠네요.)

  • psNoResampling - 보간하지 않음
  • psNearestNeighbor - 픽셀경계면의 색상을 섞지 않음 (지글지글거리게 됩니다. 안티 앨리어싱이 안먹은 느낌?)
  • psBilinear - 리니어하게 보간을 합니다. 선형보간이라 하는데 그냥 평범한 보간 법이라 보시면 됩니다.
  • psBicubic - 선형 보간에서 하나의 축을 더하여 보간을 한 것으로 좀더 자연스러운 보간이 됩니다. (추천)
  • psBicubicSharper - bicubic 을 기본으로 하며 좀더 선명한 이미지를 얻을 수 있습니다.(축소할 때 추천)
  • psBicubicSmoother - bicubic 을 기본으로 하며 좀더 부드러운 이미지를 얻을 수 있습니다. (확대할 때 추천)

요렇게 필요한 옵션으로 리사이즈를 하시면 됩니다. 보통 Bicubic 으로 하지만 저는 필요에 따라 옵션을 바꾸며 사용합니다. psNearestNeighbor 같은 경우는 어떨때 사용할까요? 3D 텍스처용 맵을 만들어 놓고 리사이즈가 필요할때 해당 옵션을 이용하여 alpha 채널을 리사이즈하면 알파채널 경계 영역이 흐릿해지는 현상을 막을 수 있습니다.

 

강좌가 좀 딴길로 샜습니다. ^^

 

끝으로 조금더 컨트롤 하는 예제를 보여드리고 마무리 하겠습니다.

  • 지정한 크기와 이름을 갖도록 설정하고 싶다.
  • 엑셀에서 배경 색상을 지정하고 싶다.
  • 텍스트 레이어를 만들어서 추가하고 싶다.

요정도만 해볼까요?

요런 내용으로 PSD 파일을 자동으로 생성해 보겠습니다.

 

코드를 작성해보면 아래와 같습니다.

Sub usingPhotoshop()

    Dim psApp As Object
    Dim psdDoc
    Dim bgColor
    Dim fontColor
    Dim cellColor
    Dim txtLayer
    
    Set psApp = CreateObject("Photoshop.Application")
    
    psApp.Documents.Add
    
    Set psdDoc = psApp.ActiveDocument
    
    psdDoc.ResizeImage CInt(Cells(2, 2).Value), CInt(Cells(2, 3).Value), 72, 4
    
    '셀 색상을 HEX 값으로 받아오기'
    cellColor = Right("000000" & Hex(Cells(2, 4).Interior.Color), 6)
    
    'bgColor 라는 SolidColor 개체를 만들어서 Hex -> DEC -> Doble 로 변환하여 적용하기'
    Set bgColor = CreateObject("Photoshop.SolidColor")
    bgColor.RGB.HexValue = cellColor
    
    bgColor.RGB.red = CDbl(WorksheetFunction.Hex2Dec(Right(cellColor, 2)))
    bgColor.RGB.green = CDbl(WorksheetFunction.Hex2Dec(Mid(cellColor, 3, 2)))
    bgColor.RGB.blue = CDbl(WorksheetFunction.Hex2Dec(Left(cellColor, 2)))
    
    '화면 전체 선택한 뒤 색상 칠하기'
    psdDoc.Selection.SelectAll
    psdDoc.Selection.Fill bgColor
    
    '폰트 색상용 색상 개체 만들어 색상 설정하기'
    cellColor = Right("000000" & Hex(Cells(2, 5).Interior.Color), 6)
    
    Set fontColor = CreateObject("Photoshop.SolidColor")
    fontColor.RGB.red = CDbl(WorksheetFunction.Hex2Dec(Right(cellColor, 2)))
    fontColor.RGB.green = CDbl(WorksheetFunction.Hex2Dec(Mid(cellColor, 3, 2)))
    fontColor.RGB.blue = CDbl(WorksheetFunction.Hex2Dec(Left(cellColor, 2)))
    
    '텍스트 레이어 추가해주기'
    Set txtLayer = psdDoc.artlayers.Add
    txtLayer.kind = 2
    txtLayer.Name = "my Text"
    
    '만들어진 텍스트 레이어에 텍스트 추가하기'
    Set txtItem = psdDoc.artlayers("my Text").textitem
    txtItem.Font = "MalgunGothicBold"
    txtItem.Size = 36
    txtItem.Color = fontColor
    txtItem.contents = Cells(2, 6).Value
    
    'PSD 파일로 저장하기'
    psdDoc.SaveAs ("d:\" & Cells(2, 1).Value & ".psd")
    
End Sub

와우 엄청 길어졌네요. 순서대로 주석을 읽어 보시면 내용은 이해가 되실텐데요. 새로운 명령어 들이 많이 나왔죠? 한번 실습한다 생각하시고 따라서 코딩을 해보신 다음 찬찬히 생각해보시면 이해가 되실거에요. 좀 생소한 코드도 있기는 하지만 어렵지는 않습니다. 실행해보면 아래와 같이 됩니다.

 

샘플 코드 만드는데 생각했던 것보다 오려걸렸네요. 꼭 필요하신 분에게 도움이 되었으면 합니다.

위에 저는 단순히 한줄만 제가 필요한 내용을 엑셀에 기재하여 PSD 파일을 만들었지만 저런 조건들이 이제 수백개가 되어도 자동으로 모두 만들 수 있는 여건이 되었습니다. 반복 문을 조금 응용하면 되겠죠?

이미지를 엑셀로 붙여넣은 포스트 기억나시나요? 해당 이미지를 열어서 셀 크기로 저장한뒤 다시 붙여 넣는 것도 가능하겠고, 여러 이미지를 하나의 포토샵에 레이어로 추가하는 것도 가능하겠으며 이미지에 파일명을 텍스트로 추가하는 것도 역시 쉽게 가능할 것 같습니다.

어렵다고 생각 마시고 바로 도전해 보시기 바랍니다.

뎃글, 공감 은 블로그 작성자에게 큰 힘이 된답니다. 
도움이 되었다 생각되시면 클릭!!  부탁드려요~

 

엑셀로 포토샵 자동화 가능?

네. 쌉가능입니다. ㅋ

반응형
반응형

엑셀을 사용하는 목적 자체가 근본적으로 문서를 만들기 위함은 아니다 보니 세로 또는 가로로 굉장히 길게 데이터가 나열되어 있는 경우가 많습니다. 우리는 vba 를 이용하여 자동화를 할 것이기 때문에 어디부터 어디까지 자동화를 할것인지 영역을 설정을 해주어야 할 필요가 있는데요. 일반적으로 제가 자주 사용하는 패턴을 몇개 올려보려 합니다.

 

우선 alt+F11 을 눌러 vba 편집창을 열어줍니다.

 

 

좌측 트리구조에서 현재 엑셀 파일에 포함되어 있는 시트의 이름과 마지막에 ThisWorkbook 이라는 트리가 보일텐데요. 저는 보통 ThisWorkbook 라는 부분에 코드를 작성합니다. 왜나하면 현재 엑셀 파일내의 모든 시트에 자동화를 하기에 적당한 위치이기 때문입니다. 대신 스크립트 동작을 위하여 어떤 시트에서 동작을 할 것인지를 설정 해 주어야 합니다. 해당 위치를 더블클릭하면 스크립트 에디터 창이 열리게 됩니다.

 

아래는 제가 가장 많이 사용하는 패턴의 시트, 영역 설정하는 코드 입니다.

Sub checkError()

    Dim asht As Worksheet
    Dim rngA As Range
    
    Set asht = ActiveSheet
    
    Set rngA = asht.Range("a3")
    Set rngA = asht.Range(rngA, rngA.End(xlDown))
        
End Sub

Dim 은 엑셀에서 변수를 정의할때 사용하는 명령어 고요. Dim 뒤에 있는 asht 가 변수 명이 되겠습니다.

뒤에 As Worksheet 라는 것은 앞에 선언한 asht 가 워크시트를 저장할 변수임을 알려주게 됩니다.

 

역시 rngA 는 변수이며 Range 즉 엑셀의 어떤 영역을 지칭합니다. 그 아래에 set 으로 시작하는 부분에서 위에 정의한 변수에 실제 유의미한 정보를 설정해 주게 됩니다.

 

asht 는 현재 활성화된 워크시트를,

rngA 는 일단 현재 시트의 a3 칸을 지정한 뒤 다시 a3 으로부터 아래 마지막까지를 설정하는 명령입니다.

 

이렇게 하면 특정 위치로부터 행의 가장 아래까지 설정이 가능합니다. 행이 몇행까지 있는지 알지 못하거나 경우에따라 변경이 될 수 있는 상황에서 이렇게 작성하면 일일이 코드 수정을 하지 않아도 되는 장점이 있습니다. 

 

이런식으로 작성할 때 가끔 문제가 발생할 수 있는 여지가 있는데요, a 열에 어떤 행이 중간에 값이 비어 있을때 입니다. Range.End(xlDown) 이라는 명령은 값이 있는 마지막까지를 탐색하기 때문이죠. 매번 마지막 행을 확인할 필요가 없으니 편리한 기능이기는 한데 기준이 되는 a 열의 중간에 값이 비어있을 수도 있다면 이렇게는 안됩니다. (중간에 빈칸이 필요한 경우 의미 없는 문자열(예> N/A)을 삽입하여 자동화 시 걸러내는 방법을 사용하는 것이 좋습니다)

 

만약 시작부터 끝을 알고 있는 경우, 또는 빈 칸이 포함되어 있는 경우 아래와 같이 정의를 해주어야 하죠.

 

Dim rngA As Range
Set rngA = asht.Range("a3","a3000")

이렇게 하면 a3 ~ a3000 까지 각 셀에 값이 있건 없건 영역으로 설정이 됩니다.

 

제가 소스 코드를 ThisWorkbook 에 작성을 한다고 하였고 그 때문에 스크립트가 동작할 시트가 어떤 시트인지를 지정해주어야 한다고 하였는데요. 만약 특정 시트에 바로 스크립트를 작성할 것이라면 아래와 같이 간단하게 영역을 설정 할 수 있습니다.

 

Sub checkError()

    Dim rngA As Range
    Set rngA = [a3:a3000]

End Sub

바로 대괄호 [ ] 를 이용하여 설정하는 방법입니다. 동일하게 a3 ~ a3000 까지를 설정하는 코드입니다. 시트는 현재 이미 선택이 되어 있기 때문에 별도로 시트를 설정할 필요가 없죠. 

 

특정 셀 하나를 설정하려면 아래와 같이하면 됩니다.

Sub checkError()

    Dim rngA As Range
    Set rngA = [a125]

End Sub

 

저는 대부분의 자동화 코드를 이런식으로 시작합니다. 

그 아래 부분에 For 구문을 이용하여 필요한 기능을 적용할 수 있습니다.

 

 

다음 편에서는 For 구문을 이용하여 반복 처리하는 부분과 offset 이라는 기능을 이용하여 선택된 셀로 부터 특정 위치에 있는 셀을 지정하는 방법을 알아보겠습니다.

 

뎃글, 공감 은 블로그 작성자에게 큰 힘이 된답니다. 
도움이 되었다 생각되시면 클릭!!  부탁드려요~

 

2019/07/12 - [DEV/VBA] - [vba] For 구문 이용하기와 offset 사용 해 보기

 

[vba] For 구문 이용하기와 offset 사용 해 보기

엑셀에서 자동화를 한다는건 사실 이유가 뻔 합니다. 처리해야 할 셀이 엄청나게 많거나 일반적인 함수로 연산이 불가한 경우 입니다. 그 중 오늘은 엄청나게 많은 데이터에 대한 자동화 처리를 위하여 반복문을..

diy-dev-design.tistory.com

 

2020/02/05 - [DEV/VBA] - [VBA] 셀의 넓이를 픽셀로 지정하기 (set column width by pixels)

 

[VBA] 셀의 넓이를 픽셀로 지정하기 (set column width by pixels)

정말 지긋지긋한 마이크로 소프트 입니다. 저는 VBA 를 하면서 이번 포스트 내용만큼 어의가 없는 경우는 없었던거 같습니다. 왜 때문이냐고요? 바로 아주 단순한 셀 넓이를 VBA 를 이용하여 지정하는 방법 때문입..

diy-dev-design.tistory.com

 

반응형

+ Recent posts