mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-06 12:19:47 +00:00
Changed docs_gen.py to not need ImageMagick or cmp
This commit is contained in:
parent
b0b4e026a8
commit
8712d246c9
2 changed files with 125 additions and 112 deletions
|
@ -8,11 +8,22 @@ import sys
|
||||||
import math
|
import math
|
||||||
import random
|
import random
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import filecmp
|
||||||
import dbm.gnu
|
import dbm.gnu
|
||||||
import os.path
|
import os.path
|
||||||
|
import platform
|
||||||
import argparse
|
import argparse
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from PIL import Image, ImageChops
|
||||||
|
|
||||||
|
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
OPENSCAD = "/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD"
|
||||||
|
GIT = "/usr/local/bin/git"
|
||||||
|
else:
|
||||||
|
OPENSCAD = "/usr/local/bin/openscad"
|
||||||
|
GIT = "git"
|
||||||
|
|
||||||
|
|
||||||
def get_header_link(name):
|
def get_header_link(name):
|
||||||
|
@ -75,6 +86,86 @@ def get_comment_block(lines, prefix, blanks=1):
|
||||||
return (lines, out)
|
return (lines, out)
|
||||||
|
|
||||||
|
|
||||||
|
def image_compare(file1, file2):
|
||||||
|
img1 = Image.open(file1)
|
||||||
|
img2 = Image.open(file2)
|
||||||
|
if img1.size != img2.size or img1.getbands() != img2.getbands():
|
||||||
|
return False
|
||||||
|
diff = ImageChops.difference(img1, img2).histogram()
|
||||||
|
sq = (value * (i % 256) ** 2 for i, value in enumerate(diff))
|
||||||
|
sum_squares = sum(sq)
|
||||||
|
rms = math.sqrt(sum_squares / float(img1.size[0] * img1.size[1]))
|
||||||
|
return rms<100
|
||||||
|
|
||||||
|
|
||||||
|
def image_resize(infile, outfile, newsize=(320,240)):
|
||||||
|
im = Image.open(infile)
|
||||||
|
im.thumbnail(newsize, Image.ANTIALIAS)
|
||||||
|
im.save(outfile)
|
||||||
|
|
||||||
|
|
||||||
|
def make_animated_gif(imgfiles, outfile, size):
|
||||||
|
imgs = []
|
||||||
|
for file in imgfiles:
|
||||||
|
img = Image.open(file)
|
||||||
|
img.thumbnail(size, Image.ANTIALIAS)
|
||||||
|
imgs.append(img)
|
||||||
|
imgs[0].save(
|
||||||
|
outfile,
|
||||||
|
save_all=True,
|
||||||
|
append_images=imgs[1:],
|
||||||
|
duration=250,
|
||||||
|
loop=0
|
||||||
|
)
|
||||||
|
|
||||||
|
def git_checkout(filename):
|
||||||
|
# Pull previous committed image from git, if it exists.
|
||||||
|
gitcmd = [GIT, "checkout", filename]
|
||||||
|
p = subprocess.Popen(gitcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
|
||||||
|
err = p.stdout.read()
|
||||||
|
|
||||||
|
|
||||||
|
def run_openscad_script(libfile, infile, imgfile, imgsize=(320,240), eye=None, show_edges=False, render=False):
|
||||||
|
scadcmd = [
|
||||||
|
OPENSCAD,
|
||||||
|
"-o", imgfile,
|
||||||
|
"--imgsize={},{}".format(imgsize[0]*2, imgsize[1]*2),
|
||||||
|
"--hardwarnings",
|
||||||
|
"--projection=o",
|
||||||
|
"--autocenter",
|
||||||
|
"--viewall"
|
||||||
|
]
|
||||||
|
if eye is not None:
|
||||||
|
scadcmd.extend(["--camera", eye+",0,0,0"])
|
||||||
|
if show_edges:
|
||||||
|
scadcmd.extend(["--view=axes,scales,edges"])
|
||||||
|
else:
|
||||||
|
scadcmd.extend(["--view=axes,scales"])
|
||||||
|
if render: # Force render
|
||||||
|
scadcmd.extend(["--render", ""])
|
||||||
|
scadcmd.append(infile)
|
||||||
|
p = subprocess.Popen(scadcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
||||||
|
(stdoutdata, stderrdata) = p.communicate(None)
|
||||||
|
res = p.returncode
|
||||||
|
if res != 0 or b"ERROR:" in stderrdata or b"WARNING:" in stderrdata:
|
||||||
|
print("%s"%stderrdata)
|
||||||
|
print("////////////////////////////////////////////////////")
|
||||||
|
print("// {}: {} for {}".format(libfile, infile, imgfile))
|
||||||
|
print("////////////////////////////////////////////////////")
|
||||||
|
print(script)
|
||||||
|
print("////////////////////////////////////////////////////")
|
||||||
|
print("")
|
||||||
|
with open("FAILED.scad", "w") as f:
|
||||||
|
print("////////////////////////////////////////////////////", file=f)
|
||||||
|
print("// {}: {} for {}".format(libfile, infile, imgfile), file=f)
|
||||||
|
print("////////////////////////////////////////////////////", file=f)
|
||||||
|
print(script, file=f)
|
||||||
|
print("////////////////////////////////////////////////////", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
sys.exit(-1)
|
||||||
|
return imgfile
|
||||||
|
|
||||||
|
|
||||||
class ImageProcessing(object):
|
class ImageProcessing(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.examples = []
|
self.examples = []
|
||||||
|
@ -103,24 +194,18 @@ class ImageProcessing(object):
|
||||||
db[key] = hash
|
db[key] = hash
|
||||||
|
|
||||||
def gen_example_image(self, db, libfile, imgfile, code, extype):
|
def gen_example_image(self, db, libfile, imgfile, code, extype):
|
||||||
OPENSCAD = "/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD"
|
|
||||||
GIT = "/usr/local/bin/git"
|
|
||||||
CONVERT = "/usr/local/bin/convert"
|
|
||||||
COMPARE = "/usr/local/bin/compare"
|
|
||||||
|
|
||||||
if extype == "NORENDER":
|
if extype == "NORENDER":
|
||||||
return
|
return
|
||||||
|
|
||||||
print(" {}".format(imgfile))
|
print(" {}".format(imgfile), end='')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
scriptfile = "tmp_{0}.scad".format(imgfile.replace(".", "_"))
|
scriptfile = "tmp_{0}.scad".format(imgfile.replace(".", "_"))
|
||||||
targimgfile = self.imgroot + imgfile
|
targimgfile = self.imgroot + imgfile
|
||||||
newimgfile = self.imgroot + "_new_" + imgfile
|
newimgfile = self.imgroot + "_new_" + imgfile
|
||||||
|
|
||||||
# Pull previous committed image from git, if it exists.
|
# Pull previous committed image from git, if it exists.
|
||||||
gitcmd = [GIT, "checkout", targimgfile]
|
git_checkout(targimgfile)
|
||||||
p = subprocess.Popen(gitcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
|
|
||||||
err = p.stdout.read()
|
|
||||||
|
|
||||||
m = hashlib.sha256()
|
m = hashlib.sha256()
|
||||||
m.update(extype.encode("utf8"))
|
m.update(extype.encode("utf8"))
|
||||||
|
@ -129,6 +214,7 @@ class ImageProcessing(object):
|
||||||
hash = m.digest()
|
hash = m.digest()
|
||||||
key = "{0} - {1}".format(libfile, imgfile)
|
key = "{0} - {1}".format(libfile, imgfile)
|
||||||
if key in db and db[key] == hash and not self.force:
|
if key in db and db[key] == hash and not self.force:
|
||||||
|
print("")
|
||||||
return
|
return
|
||||||
|
|
||||||
stdlibs = ["std.scad", "debug.scad"]
|
stdlibs = ["std.scad", "debug.scad"]
|
||||||
|
@ -146,11 +232,14 @@ class ImageProcessing(object):
|
||||||
f.write(script)
|
f.write(script)
|
||||||
|
|
||||||
if "Big" in extype:
|
if "Big" in extype:
|
||||||
imgsize = [640, 480]
|
imgsize = (640, 480)
|
||||||
elif "Med" in extype or "distribute" in script or "show_anchors" in script:
|
elif "Med" in extype or "distribute" in script or "show_anchors" in script:
|
||||||
imgsize = [480, 360]
|
imgsize = (480, 360)
|
||||||
else: # Small
|
else: # Small
|
||||||
imgsize = [320, 240]
|
imgsize = (320, 240)
|
||||||
|
|
||||||
|
show_edges = "Edges" in extype
|
||||||
|
render = "FR" in extype
|
||||||
|
|
||||||
tmpimgs = []
|
tmpimgs = []
|
||||||
if "Spin" in extype:
|
if "Spin" in extype:
|
||||||
|
@ -162,126 +251,50 @@ class ImageProcessing(object):
|
||||||
500*math.sin(arad),
|
500*math.sin(arad),
|
||||||
500 if "Flat" in extype else 500*math.sin(arad)
|
500 if "Flat" in extype else 500*math.sin(arad)
|
||||||
)
|
)
|
||||||
scadcmd = [
|
run_openscad_script(
|
||||||
OPENSCAD,
|
libfile, scriptfile, tmpimgfile,
|
||||||
"-o", tmpimgfile,
|
imgsize=(imgsize[0]*2,imgsize[1]*2),
|
||||||
"--imgsize={},{}".format(imgsize[0]*2, imgsize[1]*2),
|
eye=eye,
|
||||||
"--hardwarnings",
|
show_edges=show_edges,
|
||||||
"--projection=o",
|
render=render
|
||||||
"--autocenter",
|
)
|
||||||
"--viewall",
|
|
||||||
"--camera", eye+",0,0,0"
|
|
||||||
]
|
|
||||||
if "Edges" in extype: # Force render
|
|
||||||
scadcmd.extend(["--view=axes,scales,edges"])
|
|
||||||
else:
|
|
||||||
scadcmd.extend(["--view=axes,scales"])
|
|
||||||
if "FR" in extype: # Force render
|
|
||||||
scadcmd.extend(["--render", ""])
|
|
||||||
scadcmd.append(scriptfile)
|
|
||||||
p = subprocess.Popen(scadcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
|
||||||
(stdoutdata, stderrdata) = p.communicate(None)
|
|
||||||
res = p.returncode
|
|
||||||
if res != 0 or b"ERROR:" in stderrdata or b"WARNING:" in stderrdata:
|
|
||||||
print("%s"%stderrdata)
|
|
||||||
print("////////////////////////////////////////////////////")
|
|
||||||
print("// {}: {} for {}".format(libfile, scriptfile, imgfile))
|
|
||||||
print("////////////////////////////////////////////////////")
|
|
||||||
print(script)
|
|
||||||
print("////////////////////////////////////////////////////")
|
|
||||||
print("")
|
|
||||||
sys.exit(-1)
|
|
||||||
tmpimgs.append(tmpimgfile)
|
tmpimgs.append(tmpimgfile)
|
||||||
|
print(".", end='')
|
||||||
|
sys.stdout.flush()
|
||||||
else:
|
else:
|
||||||
tmpimgfile = self.imgroot + "tmp_" + imgfile
|
tmpimgfile = self.imgroot + "tmp_" + imgfile
|
||||||
scadcmd = [
|
eye = "0,0,500" if "2D" in extype else None
|
||||||
OPENSCAD,
|
run_openscad_script(
|
||||||
"-o", tmpimgfile,
|
libfile, scriptfile, tmpimgfile,
|
||||||
"--imgsize={},{}".format(imgsize[0]*2, imgsize[1]*2),
|
imgsize=(imgsize[0]*2,imgsize[1]*2),
|
||||||
"--hardwarnings",
|
eye=eye,
|
||||||
"--projection=o",
|
show_edges=show_edges,
|
||||||
"--autocenter",
|
render=render
|
||||||
"--viewall"
|
)
|
||||||
]
|
|
||||||
if "2D" in extype: # 2D viewpoint
|
|
||||||
scadcmd.extend(["--camera", "0,0,0,0,0,0,500"])
|
|
||||||
if "Edges" in extype: # Force render
|
|
||||||
scadcmd.extend(["--view=axes,scales,edges"])
|
|
||||||
else:
|
|
||||||
scadcmd.extend(["--view=axes,scales"])
|
|
||||||
if "FR" in extype: # Force render
|
|
||||||
scadcmd.extend(["--render", ""])
|
|
||||||
scadcmd.append(scriptfile)
|
|
||||||
|
|
||||||
p = subprocess.Popen(scadcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
|
||||||
(stdoutdata, stderrdata) = p.communicate(None)
|
|
||||||
res = p.returncode
|
|
||||||
if res != 0 or b"ERROR:" in stderrdata or b"WARNING:" in stderrdata:
|
|
||||||
print("%s"%stderrdata)
|
|
||||||
print("////////////////////////////////////////////////////")
|
|
||||||
print("// {}: {} for {}".format(libfile, scriptfile, imgfile))
|
|
||||||
print("////////////////////////////////////////////////////")
|
|
||||||
print(script)
|
|
||||||
print("////////////////////////////////////////////////////")
|
|
||||||
print("")
|
|
||||||
with open("FAILED.scad", "w") as f:
|
|
||||||
print("////////////////////////////////////////////////////", file=f)
|
|
||||||
print("// {}: {} for {}".format(libfile, scriptfile, imgfile), file=f)
|
|
||||||
print("////////////////////////////////////////////////////", file=f)
|
|
||||||
print(script, file=f)
|
|
||||||
print("////////////////////////////////////////////////////", file=f)
|
|
||||||
print("", file=f)
|
|
||||||
sys.exit(-1)
|
|
||||||
tmpimgs.append(tmpimgfile)
|
tmpimgs.append(tmpimgfile)
|
||||||
|
|
||||||
if not self.keep_scripts:
|
if not self.keep_scripts:
|
||||||
os.unlink(scriptfile)
|
os.unlink(scriptfile)
|
||||||
|
|
||||||
if len(tmpimgs) == 1:
|
if len(tmpimgs) == 1:
|
||||||
cnvcmd = [
|
image_resize(tmpimgfile, newimgfile, imgsize)
|
||||||
CONVERT,
|
|
||||||
tmpimgfile,
|
|
||||||
"-resize", "{}x{}".format(imgsize[0], imgsize[1]),
|
|
||||||
newimgfile
|
|
||||||
]
|
|
||||||
res = subprocess.call(cnvcmd)
|
|
||||||
if res != 0:
|
|
||||||
sys.exit(-1)
|
|
||||||
os.unlink(tmpimgs.pop(0))
|
os.unlink(tmpimgs.pop(0))
|
||||||
else:
|
else:
|
||||||
cnvcmd = [
|
make_animated_gif(tmpimgs, newimgfile, size=imgsize)
|
||||||
CONVERT,
|
|
||||||
"-delay", "25",
|
|
||||||
"-loop", "0",
|
|
||||||
"-coalesce",
|
|
||||||
"-scale", "{}x{}".format(imgsize[0], imgsize[1]),
|
|
||||||
"-fuzz", "2%",
|
|
||||||
"+dither",
|
|
||||||
"-layers", "Optimize",
|
|
||||||
"+map"
|
|
||||||
]
|
|
||||||
cnvcmd.extend(tmpimgs)
|
|
||||||
cnvcmd.append(newimgfile)
|
|
||||||
res = subprocess.call(cnvcmd)
|
|
||||||
if res != 0:
|
|
||||||
sys.exit(-1)
|
|
||||||
for tmpimg in tmpimgs:
|
for tmpimg in tmpimgs:
|
||||||
os.unlink(tmpimg)
|
os.unlink(tmpimg)
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
# Time to compare image.
|
# Time to compare image.
|
||||||
if not os.path.isfile(targimgfile):
|
if not os.path.isfile(targimgfile):
|
||||||
print(" NEW IMAGE\n")
|
print(" NEW IMAGE\n")
|
||||||
os.rename(newimgfile, targimgfile)
|
os.rename(newimgfile, targimgfile)
|
||||||
else:
|
else:
|
||||||
if targimgfile.endswith(".gif"):
|
if targimgfile.endswith(".gif"):
|
||||||
cmpcmd = ["cmp", "-s", newimgfile, targimgfile]
|
issame = filecmp.cmp(targimgfile, newimgfile, shallow=False)
|
||||||
res = subprocess.call(cmpcmd)
|
|
||||||
issame = res == 0
|
|
||||||
else:
|
else:
|
||||||
cmpcmd = [COMPARE, "-metric", "MAE", newimgfile, targimgfile, "null:"]
|
issame = image_compare(targimgfile, newimgfile);
|
||||||
p = subprocess.Popen(cmpcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
|
|
||||||
resbin = p.stdout.read().strip()
|
|
||||||
issame = resbin == b'0 (0)'
|
|
||||||
if issame:
|
if issame:
|
||||||
os.unlink(newimgfile)
|
os.unlink(newimgfile)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
BOSL_VERSION = [2,0,57];
|
BOSL_VERSION = [2,0,58];
|
||||||
|
|
||||||
|
|
||||||
// Section: BOSL Library Version Functions
|
// Section: BOSL Library Version Functions
|
||||||
|
|
Loading…
Reference in a new issue