「ガッツでC.G.!サポートツール」で LINE&PAINT CG を描くと、1データあたり 100KB~200KBぐらいになります。8ビットPCの頃に比べると、超贅沢なデータ量ですね。
M5Atom でCG描画アプリを実行するときは、このデータをフラッシュメモリ(SPIFFS)に格納するわけですが、フラッシュメモリのデータ格納容量が2MB程度しか確保できなくて、そうすると 100KB だと 20枚程度が限界になってしまうのですよね。
MI68 で展示していた PIC 画像表示とLINE&PAINT CG です。
PIC 画像は電脳倶楽部に収録されていたものです。LINE&PAINT データは自分で画像をトレースして作成しました。 pic.twitter.com/7e9lq85A1R— Nochi(ドウスル?▮) (@shikarunochi) October 13, 2022
外付けでSDカードを接続するのもアリですが、ミニチュアモニタで使っている 240×240 1.3インチディスプレイはCSピンを持ってないため、SDカードとLCDでSPI接続共有するのが難しく、M5Atom だとピンが足りないわけです。
そんなわけで、少しでもファイルサイズを小さくしたい。
「ガッツでC.G.!サポートツール」は線を引くときにかなり贅沢にポイント座標を取得してます。
こういう風になってるところも多いです。
このとき、(X1,Y1)-(X2,Y2)の角度と(X1,Y1)-(X3,Y3)の角度が同じであれば(X2,Y2)は省略することが可能ですね。
生成済みの描画データに対して、この処理を行うツールを Python で書いてみました。
import sys import os if len(sys.argv) > 1: fileName = sys.argv[1] else: fileName = "MagicalMirai2018.dat" #print("ファイル名を指定してください") #sys.exit() cgData = open(fileName, encoding="utf-8") file, ext = os.path.splitext(fileName) dietData = open(file + "_diet.dat", mode='w', encoding="utf-8") line = cgData.readline() #1行目はタイトル dietData.write(line) #直線になっている場合、途中の点を省略する。 lineMode = False while line: line = cgData.readline() print("line:" + line) lineData = line.rstrip().split(',') #-10から始まっていたら、LINEモード。LINEは1行1LINE if len(lineData) == 0: continue if line.startswith('-10'): lineMode = True dietData.write(lineData.pop(0)) dietData.write(',') elif line.startswith('-1'): lineMode = False dietData.write(line) elif lineMode == False: dietData.write(line) if lineMode == True: dietData.write(lineData.pop(0)) #color dietData.write(',') index = 0 while(1): if index + 5 >= len(lineData): dietData.write(",".join(lineData)) dietData.write("\n") break #(x1,y1)-(x2,y2)の傾きと、(x1,y1)-(x3,y3)の傾きが同じであれば(x2,y2)を削除できる。 x1 = int(lineData[index + 0]) y1 = int(lineData[index + 1]) x2 = int(lineData[index + 2]) y2 = int(lineData[index + 3]) x3 = int(lineData[index + 4]) y3 = int(lineData[index + 5]) if(y1 - y2 == 0) and (y1 - y3 == 0): #両方傾きゼロだけ特別扱い del lineData[index+2] del lineData[index+2] #詰まっているので同じINDEX elif(y1 - y2 == 0) or (y1 - y3 == 0): #分母ゼロなので割れない index = index + 2 elif (x1-x2) / (y1 - y2) == (x1-x3) / (y1 -y3): del lineData[index+2] del lineData[index+2] #詰まっているので同じINDEX else: index = index + 2 dietData.close() cgData.close()
元ファイル:MagicalMirai2018.dat : 194,551 byte
実行後ファイル:MagicalMirai2018.diet.dat :126,695 byte
小さくなった!(その分、描画の時も少々クイックになります)
最初からこの処理を「ガッツでC.G.!サポートツール」の線引き処理に組み込んどけばよいのですよね…。
var point = [Math.round(mouse.x),Math.round(mouse.y)]; //前回と傾きが同じだった場合、前回のポイントは削除する。 if(curDrawData.length >=2){ point1 = curDrawData[curDrawData.length-2]; point2 = curDrawData[curDrawData.length-1]; x1 = point1[0]; y1 = point1[1]; x2 = point2[0]; y2 = point1[1]; x3 = point[0]; y3 = point[1]; if((y1 - y2 == 0) && (y1 - y3 == 0)){ //両方傾きゼロだけ特別扱い curDrawData.pop(); }else if((y1 - y2 == 0) || (y1 - y3 == 0)){ //分母ゼロなので割れない }else if((x1-x2) / (y1 - y2) == (x1 - x3) / (y1 - y3)){ curDrawData.pop(); } } curDrawData.push(point);
…ということで、こちらも先ほど組み込み終わりました!
(2024.05追記)
その後…上の方法だと元データとずれが発生することが発覚し、方式を変更しました。
ということで、「(X1,Y1)-(X2,Y2)と(X2,Y2)-(X3,Y3)で実際に描画するドット集合と、(X1,Y1)-(X3,Y3)で描画するドット集合が一致する場合に(X2,Y2)は省略可能」のロジックに変えてみました。これでバッチリ!のはずだ! (色味の違いは撮影状況によるものなので気にしないで…) pic.twitter.com/RYFxA3vSHv
— Nochi(ドウスル?▮) (@shikarunochi) May 8, 2024
変更後のプログラム。
import sys import os def linePixelSet(x1,y1,x2,y2): startX = x1 startY = y1 endX = x2 endY = y2 pixelSet = {(startX,startY)} dx = abs(endX - startX) sx = 1 if (startX < endX)else -1 dy = abs(endY - startY) sy = 1 if (startY < endY)else -1 err = int((dx if (dx > dy) else -dy) / 2) while True: pixelSet.add((startX,startY)) if startX == endX and startY == endY: break e2 = err if e2 > -dx: err = err - dy startX = startX + sx if e2 < dy : err = err + dx startY = startY + sy return pixelSet if len(sys.argv) > 1: fileName = sys.argv[1] else: fileName = "MagicalMirai2018.dat" #print("ファイル名を指定してください") #sys.exit() cgData = open(fileName, encoding="utf-8") file, ext = os.path.splitext(fileName) dietData = open(file + "_diet.dat", mode='w', encoding="utf-8") line = cgData.readline() #1行目はタイトル dietData.write(line) #直線になっている場合、途中の点を省略する。 lineMode = False while line: line = cgData.readline() print("line:" + line) lineData = line.rstrip().split(',') #-10から始まっていたら、LINEモード。LINEは1行1LINE if len(lineData) == 0: continue if line.startswith('-10'): lineMode = True dietData.write(lineData.pop(0)) dietData.write(',') elif line.startswith('-1'): lineMode = False dietData.write(line) elif lineMode == False: dietData.write(line) if lineMode == True: dietData.write(lineData.pop(0)) #color dietData.write(',') index = 0 while(1): if index + 5 >= len(lineData): dietData.write(",".join(lineData)) dietData.write("\n") break #(x1,y1)-(x2,y2)の傾きと、(x1,y1)-(x3,y3)の傾きが同じであれば(x2,y2)を削除できる。 #(x1,y1)-(x2,y2)と(x2,y2)-(x3,y3)で塗るドットと、(x1,y1)-(x3,y3)で塗るドットが同一であれば(x2,y2)を削除できる。 x1 = int(lineData[index + 0]) y1 = int(lineData[index + 1]) x2 = int(lineData[index + 2]) y2 = int(lineData[index + 3]) x3 = int(lineData[index + 4]) y3 = int(lineData[index + 5]) oldPixelSet = linePixelSet(x1,y1,x2,y2)|linePixelSet(x2,y2,x3,y3) newPixelSet = linePixelSet(x1,y1,x3,y3) if oldPixelSet == newPixelSet: #集合の比較 del lineData[index+2] del lineData[index+2] #詰まっているので同じINDEX else: index = index + 2 dietData.close() cgData.close()
これでバッチリ!!!