else
This commit is contained in:
parent
6e64483523
commit
0896ba96e6
20 changed files with 1826 additions and 1269 deletions
|
@ -9,10 +9,24 @@ import os
|
|||
from voussoirkit import bytestring
|
||||
|
||||
|
||||
# 128 bits
|
||||
BLOCK_SIZE = 32
|
||||
|
||||
KEY_SIZE = 32
|
||||
SEEK_END = 2
|
||||
|
||||
def decrypt_data(aes, data):
|
||||
data = aes.decrypt(data)
|
||||
pad_byte = data[-1:]
|
||||
data = data.rstrip(pad_byte)
|
||||
return data
|
||||
|
||||
def encrypt_data(aes, data):
|
||||
pad_byte = (data[-1] + 1) % 256
|
||||
pad_length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
|
||||
data += (bytes([pad_byte]) * pad_length)
|
||||
data = aes.encrypt(data)
|
||||
return data
|
||||
|
||||
def decrypt_file(aes, input_handle, output_handle):
|
||||
current_pos = input_handle.tell()
|
||||
input_size = input_handle.seek(0, SEEK_END) - current_pos
|
||||
|
@ -42,8 +56,7 @@ def encrypt_file(aes, input_handle, output_handle):
|
|||
last_byte = chunk[-1]
|
||||
if len(chunk) < BLOCK_SIZE:
|
||||
pad_byte = (last_byte + 1) % 256
|
||||
pad_byte = chr(pad_byte)
|
||||
pad_byte = pad_byte.encode('ascii')
|
||||
pad_byte = bytes([pad_byte])
|
||||
chunk += pad_byte * (BLOCK_SIZE - len(chunk))
|
||||
done = True
|
||||
bytes_read += len(chunk)
|
||||
|
@ -60,7 +73,7 @@ def encrypt_argparse(args):
|
|||
input_handle = open(args.input, 'rb')
|
||||
output_handle = open(args.output, 'wb')
|
||||
|
||||
password = hashit(args.password, 32)
|
||||
password = hashit(args.password)
|
||||
initialization_vector = os.urandom(16)
|
||||
aes = AES.new(password, mode=3, IV=initialization_vector)
|
||||
output_handle.write(initialization_vector)
|
||||
|
@ -71,15 +84,14 @@ def decrypt_argparse(args):
|
|||
input_handle = open(args.input, 'rb')
|
||||
output_handle = open(args.output, 'wb')
|
||||
|
||||
password = hashit(args.password, 32)
|
||||
password = hashit(args.password)
|
||||
initialization_vector = input_handle.read(16)
|
||||
aes = AES.new(password, mode=3, IV=initialization_vector)
|
||||
decrypt_file(aes, input_handle, output_handle)
|
||||
|
||||
def hashit(text, length=None):
|
||||
def hashit(text):
|
||||
h = hashlib.sha512(text.encode('utf-8')).hexdigest()
|
||||
if length is not None:
|
||||
h = h[:length]
|
||||
h = h[:BLOCK_SIZE]
|
||||
return h
|
||||
|
||||
def main(argv):
|
||||
|
@ -102,4 +114,4 @@ def main(argv):
|
|||
args.func(args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
main(sys.argv[1:])
|
||||
|
|
|
@ -23,7 +23,7 @@ FILENAME_BADCHARS = '*?"<>|\r'
|
|||
|
||||
last_request = 0
|
||||
CHUNKSIZE = 4 * bytestring.KIBIBYTE
|
||||
TIMEOUT = 600
|
||||
TIMEOUT = 60
|
||||
TEMP_EXTENSION = '.downloadytemp'
|
||||
|
||||
PRINT_LIMITER = ratelimiter.Ratelimiter(allowance=5, mode='reject')
|
||||
|
@ -35,11 +35,12 @@ def download_file(
|
|||
auth=None,
|
||||
bytespersecond=None,
|
||||
callback_progress=None,
|
||||
do_head=True,
|
||||
headers=None,
|
||||
overwrite=False,
|
||||
raise_for_undersized=True,
|
||||
timeout=None,
|
||||
verbose=False,
|
||||
**get_kwargs
|
||||
):
|
||||
headers = headers or {}
|
||||
|
||||
|
@ -61,13 +62,20 @@ def download_file(
|
|||
localname,
|
||||
auth=auth,
|
||||
bytespersecond=bytespersecond,
|
||||
callback_progress=callback_progress,
|
||||
do_head=do_head,
|
||||
headers=headers,
|
||||
overwrite=overwrite,
|
||||
raise_for_undersized=raise_for_undersized,
|
||||
timeout=timeout,
|
||||
)
|
||||
#print(plan)
|
||||
if plan is None:
|
||||
return
|
||||
|
||||
return download_plan(plan)
|
||||
|
||||
def download_plan(plan):
|
||||
localname = plan['download_into']
|
||||
directory = os.path.split(localname)[0]
|
||||
if directory != '':
|
||||
|
@ -77,7 +85,7 @@ def download_file(
|
|||
file_handle.seek(plan['seek_to'])
|
||||
|
||||
if plan['header_range_min'] is not None:
|
||||
headers['range'] = 'bytes={min}-{max}'.format(
|
||||
plan['headers']['range'] = 'bytes={min}-{max}'.format(
|
||||
min=plan['header_range_min'],
|
||||
max=plan['header_range_max'],
|
||||
)
|
||||
|
@ -89,7 +97,20 @@ def download_file(
|
|||
else:
|
||||
bytes_downloaded = 0
|
||||
|
||||
download_stream = request('get', url, stream=True, headers=headers, auth=auth, **get_kwargs)
|
||||
download_stream = request(
|
||||
'get',
|
||||
plan['url'],
|
||||
stream=True,
|
||||
auth=plan['auth'],
|
||||
headers=plan['headers'],
|
||||
timeout=plan['timeout'],
|
||||
)
|
||||
|
||||
if plan['remote_total_bytes'] is None:
|
||||
# Since we didn't do a head, let's fill this in now.
|
||||
plan['remote_total_bytes'] = int(download_stream.headers.get('Content-Length', 0))
|
||||
|
||||
callback_progress = plan['callback_progress']
|
||||
if callback_progress is not None:
|
||||
callback_progress = callback_progress(plan['remote_total_bytes'])
|
||||
|
||||
|
@ -108,7 +129,7 @@ def download_file(
|
|||
if os.devnull not in [localname, plan['real_localname']]:
|
||||
localsize = os.path.getsize(localname)
|
||||
undersized = plan['plan_type'] != 'partial' and localsize < plan['remote_total_bytes']
|
||||
if raise_for_undersized and undersized:
|
||||
if plan['raise_for_undersized'] and undersized:
|
||||
message = 'File does not contain expected number of bytes. Received {size} / {total}'
|
||||
message = message.format(size=localsize, total=plan['remote_total_bytes'])
|
||||
raise Exception(message)
|
||||
|
@ -121,12 +142,17 @@ def download_file(
|
|||
def prepare_plan(
|
||||
url,
|
||||
localname,
|
||||
auth,
|
||||
bytespersecond,
|
||||
headers,
|
||||
overwrite,
|
||||
auth=None,
|
||||
bytespersecond=None,
|
||||
callback_progress=None,
|
||||
do_head=True,
|
||||
headers=None,
|
||||
overwrite=False,
|
||||
raise_for_undersized=True,
|
||||
timeout=TIMEOUT,
|
||||
):
|
||||
# Chapter 1: File existence
|
||||
headers = headers or {}
|
||||
user_provided_range = 'range' in headers
|
||||
real_localname = localname
|
||||
temp_localname = localname + TEMP_EXTENSION
|
||||
|
@ -163,21 +189,34 @@ def prepare_plan(
|
|||
temp_headers = headers
|
||||
temp_headers.update({'range': 'bytes=0-'})
|
||||
|
||||
# I'm using a GET instead of an actual HEAD here because some servers respond
|
||||
# differently, even though they're not supposed to.
|
||||
head = request('get', url, stream=True, headers=temp_headers, auth=auth)
|
||||
remote_total_bytes = int(head.headers.get('content-length', 0))
|
||||
server_respects_range = (head.status_code == 206 and 'content-range' in head.headers)
|
||||
head.connection.close()
|
||||
if do_head:
|
||||
# I'm using a GET instead of an actual HEAD here because some servers respond
|
||||
# differently, even though they're not supposed to.
|
||||
head = request('get', url, stream=True, headers=temp_headers, auth=auth)
|
||||
remote_total_bytes = int(head.headers.get('content-length', 0))
|
||||
server_respects_range = (head.status_code == 206 and 'content-range' in head.headers)
|
||||
head.connection.close()
|
||||
else:
|
||||
remote_total_bytes = None
|
||||
server_respects_range = False
|
||||
|
||||
if user_provided_range and not server_respects_range:
|
||||
raise Exception('Server did not respect your range header')
|
||||
if not do_head:
|
||||
raise Exception('Cannot determine range support without the head request')
|
||||
else:
|
||||
raise Exception('Server did not respect your range header')
|
||||
|
||||
# Chapter 5: Plan definitions
|
||||
plan_base = {
|
||||
'url': url,
|
||||
'auth': auth,
|
||||
'callback_progress': callback_progress,
|
||||
'limiter': limiter,
|
||||
'headers': headers,
|
||||
'real_localname': real_localname,
|
||||
'raise_for_undersized': raise_for_undersized,
|
||||
'remote_total_bytes': remote_total_bytes,
|
||||
'timeout': timeout,
|
||||
}
|
||||
plan_fulldownload = dict(
|
||||
plan_base,
|
||||
|
@ -373,8 +412,10 @@ def download_argparse(args):
|
|||
localname=args.localname,
|
||||
bytespersecond=bytespersecond,
|
||||
callback_progress=callback,
|
||||
do_head=args.no_head is False,
|
||||
headers=headers,
|
||||
overwrite=args.overwrite,
|
||||
timeout=int(args.timeout),
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
|
@ -388,6 +429,8 @@ if __name__ == '__main__':
|
|||
parser.add_argument('-bps', '--bytespersecond', dest='bytespersecond', default=None)
|
||||
parser.add_argument('-ow', '--overwrite', dest='overwrite', action='store_true')
|
||||
parser.add_argument('-r', '--range', dest='range', default=None)
|
||||
parser.add_argument('--timeout', dest='timeout', default=TIMEOUT)
|
||||
parser.add_argument('--no-head', dest='no_head', action='store_true')
|
||||
parser.set_defaults(func=download_argparse)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
import glob
|
||||
import os
|
||||
|
||||
class Path:
|
||||
'''
|
||||
I started to use pathlib.Path, but it was too much of a pain.
|
||||
'''
|
||||
def __init__(self, path):
|
||||
path = os.path.normpath(path)
|
||||
path = os.path.abspath(path)
|
||||
self.absolute_path = path
|
||||
if isinstance(path, Path):
|
||||
self.absolute_path = path.absolute_path
|
||||
else:
|
||||
path = os.path.normpath(path)
|
||||
path = os.path.abspath(path)
|
||||
self.absolute_path = path
|
||||
|
||||
def __contains__(self, other):
|
||||
this = os.path.normcase(self.absolute_path)
|
||||
that = os.path.normcase(other.absolute_path)
|
||||
return that.startswith(this)
|
||||
return other.normcase.startswith(self.normcase)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not hasattr(other, 'absolute_path'):
|
||||
return False
|
||||
this = os.path.normcase(self.absolute_path)
|
||||
that = os.path.normcase(other.absolute_path)
|
||||
return this == that
|
||||
return self.normcase == other.normcase
|
||||
|
||||
def __hash__(self):
|
||||
return hash(os.path.normcase(self.absolute_path))
|
||||
|
@ -35,6 +35,10 @@ class Path:
|
|||
self.absolute_path = get_path_casing(self.absolute_path)
|
||||
return self.absolute_path
|
||||
|
||||
@property
|
||||
def depth(self):
|
||||
return len(self.absolute_path.split(os.sep))
|
||||
|
||||
@property
|
||||
def exists(self):
|
||||
return os.path.exists(self.absolute_path)
|
||||
|
@ -51,6 +55,10 @@ class Path:
|
|||
def is_link(self):
|
||||
return os.path.islink(self.absolute_path)
|
||||
|
||||
@property
|
||||
def normcase(self):
|
||||
return os.path.normcase(self.absolute_path)
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
parent = os.path.dirname(self.absolute_path)
|
||||
|
@ -59,10 +67,21 @@ class Path:
|
|||
|
||||
@property
|
||||
def relative_path(self):
|
||||
relative = self.absolute_path
|
||||
relative = relative.replace(os.getcwd(), '')
|
||||
relative = relative.lstrip(os.sep)
|
||||
return relative
|
||||
cwd = Path(os.getcwd())
|
||||
self.correct_case()
|
||||
if self.absolute_path == cwd:
|
||||
return '.'
|
||||
|
||||
if self in cwd:
|
||||
return self.absolute_path.replace(cwd.absolute_path, '.')
|
||||
|
||||
common = common_path([os.getcwd(), self.absolute_path], fallback=None)
|
||||
if common is None:
|
||||
return self.absolute_path
|
||||
backsteps = cwd.depth - common.depth
|
||||
backsteps = os.sep.join('..' for x in range(backsteps))
|
||||
print('hi')
|
||||
return self.absolute_path.replace(common.absolute_path, backsteps)
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
|
@ -80,6 +99,34 @@ class Path:
|
|||
return Path(os.path.join(self.absolute_path, basename))
|
||||
|
||||
|
||||
def common_path(paths, fallback):
|
||||
'''
|
||||
Given a list of file paths, determine the deepest path which all
|
||||
have in common.
|
||||
'''
|
||||
if isinstance(paths, (str, Path)):
|
||||
raise TypeError('`paths` must be a collection')
|
||||
paths = [Path(f) for f in paths]
|
||||
|
||||
if len(paths) == 0:
|
||||
raise ValueError('Empty list')
|
||||
|
||||
if hasattr(paths, 'pop'):
|
||||
model = paths.pop()
|
||||
else:
|
||||
model = paths[0]
|
||||
paths = paths[1:]
|
||||
|
||||
while True:
|
||||
if all(f in model for f in paths):
|
||||
return model
|
||||
parent = model.parent
|
||||
if parent == model:
|
||||
# We just processed the root, and now we're stuck at the root.
|
||||
# Which means there was no common path.
|
||||
return fallback
|
||||
model = parent
|
||||
|
||||
def get_path_casing(path):
|
||||
'''
|
||||
Take what is perhaps incorrectly cased input and get the path's actual
|
||||
|
@ -89,8 +136,21 @@ def get_path_casing(path):
|
|||
Ethan Furman http://stackoverflow.com/a/7133137/5430534
|
||||
xvorsx http://stackoverflow.com/a/14742779/5430534
|
||||
'''
|
||||
if isinstance(path, Path):
|
||||
path = path.absolute_path
|
||||
if not isinstance(path, Path):
|
||||
path = Path(path)
|
||||
|
||||
# Nonexistent paths don't glob correctly. If the input is a nonexistent
|
||||
# subpath of an existing path, we have to glob the existing portion first,
|
||||
# and then attach the fake portion again at the end.
|
||||
input_path = path
|
||||
while not path.exists:
|
||||
parent = path.parent
|
||||
if path == parent:
|
||||
# We're stuck at a fake root.
|
||||
return input_path.absolute_path
|
||||
path = parent
|
||||
|
||||
path = path.absolute_path
|
||||
|
||||
(drive, subpath) = os.path.splitdrive(path)
|
||||
drive = drive.upper()
|
||||
|
@ -99,11 +159,17 @@ def get_path_casing(path):
|
|||
pattern = [glob_patternize(piece) for piece in subpath.split(os.sep)]
|
||||
pattern = os.sep.join(pattern)
|
||||
pattern = drive + os.sep + pattern
|
||||
#print(pattern)
|
||||
|
||||
try:
|
||||
return glob.glob(pattern)[0]
|
||||
cased = glob.glob(pattern)[0]
|
||||
imaginary_portion = input_path.normcase
|
||||
real_portion = os.path.normcase(cased)
|
||||
imaginary_portion = imaginary_portion.replace(real_portion, '')
|
||||
imaginary_portion = imaginary_portion.lstrip(os.sep)
|
||||
cased = os.path.join(cased, imaginary_portion)
|
||||
return cased
|
||||
except IndexError:
|
||||
return path
|
||||
return input_path
|
||||
|
||||
def glob_patternize(piece):
|
||||
'''
|
||||
|
@ -111,10 +177,15 @@ def glob_patternize(piece):
|
|||
correct path name, while guaranteeing that the only result will be the correct path.
|
||||
|
||||
Special cases are:
|
||||
!, because in glob syntax, [!x] tells glob to look for paths that don't contain
|
||||
"x". [!] is invalid syntax, so we pick the first non-! character to put
|
||||
in the brackets.
|
||||
[, because this starts a capture group
|
||||
`!`
|
||||
because in glob syntax, [!x] tells glob to look for paths that don't contain
|
||||
"x", and [!] is invalid syntax.
|
||||
`[`, `]`
|
||||
because this starts a glob capture group
|
||||
|
||||
so we pick the first non-special character to put in the brackets.
|
||||
If the path consists entirely of these special characters, then the
|
||||
casing doesn't need to be corrected anyway.
|
||||
'''
|
||||
piece = glob.escape(piece)
|
||||
for character in piece:
|
||||
|
@ -124,3 +195,13 @@ def glob_patternize(piece):
|
|||
piece = piece.replace(character, replacement, 1)
|
||||
break
|
||||
return piece
|
||||
|
||||
def normalize_sep(path):
|
||||
for char in ('\\', '/'):
|
||||
if char != os.sep:
|
||||
path = path.replace(char, os.sep)
|
||||
path = path.rstrip(os.sep)
|
||||
return path
|
||||
|
||||
def system_root():
|
||||
return os.path.abspath(os.sep)
|
||||
|
|
18
Safeprint/safeprint.py
Normal file
18
Safeprint/safeprint.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
'''
|
||||
This function is slow and ugly, but I need a way to safely print unicode strings
|
||||
on systems that don't support it without crippling those who do.
|
||||
'''
|
||||
def safeprint(text, file_handle=None):
|
||||
for character in text:
|
||||
try:
|
||||
if file_handle:
|
||||
file_handle.write(character)
|
||||
else:
|
||||
print(character, end='', flush=False)
|
||||
except UnicodeError:
|
||||
if file_handle:
|
||||
file_handle.write('?')
|
||||
else:
|
||||
print('?', end='', flush=False)
|
||||
if not file_handle:
|
||||
print()
|
|
@ -290,7 +290,7 @@ def generate_random_filename(original_filename='', length=8):
|
|||
return identifier
|
||||
|
||||
def main():
|
||||
server = ThreadedServer(('', 32768), RequestHandler)
|
||||
server = ThreadedServer(('', int(sys.argv[1] or 32768)), RequestHandler)
|
||||
print('server starting')
|
||||
server.serve_forever()
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
22
Templates/timetest.py
Normal file
22
Templates/timetest.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
import time
|
||||
|
||||
LOOPS = 10000
|
||||
|
||||
def func_1():
|
||||
pass
|
||||
|
||||
def func_2():
|
||||
pass
|
||||
|
||||
|
||||
start = time.time()
|
||||
for x in range(LOOPS):
|
||||
func_1()
|
||||
end = time.time()
|
||||
print('v1', end - start)
|
||||
|
||||
start = time.time()
|
||||
for x in range(LOOPS):
|
||||
func_2()
|
||||
end = time.time()
|
||||
print('v2', end - start)
|
|
@ -36,6 +36,10 @@ def threaded_dl(urls, thread_count, filename_format=None):
|
|||
if filename_format is None:
|
||||
filename_format = '{now}_{index}_{basename}'
|
||||
filename_format = filename_format.replace('{index}', '{index:0%0dd}' % index_digits)
|
||||
if '{' not in filename_format and len(urls) > 1:
|
||||
filename_format += '_{index}'
|
||||
if '{extension}' not in filename_format:
|
||||
filename_format += '{extension}'
|
||||
now = int(time.time())
|
||||
for (index, url) in enumerate(urls):
|
||||
while len(threads) == thread_count:
|
||||
|
|
|
@ -16,6 +16,8 @@ import random
|
|||
import re
|
||||
import sys
|
||||
|
||||
from voussoirkit import safeprint
|
||||
|
||||
|
||||
def brename(transformation):
|
||||
old = os.listdir()
|
||||
|
@ -46,7 +48,7 @@ def loop(pairs, dry=False):
|
|||
line = '{old}\n{new}\n'
|
||||
line = line.format(old=x, new=y)
|
||||
#print(line.encode('utf-8'))
|
||||
print(line.encode('ascii', 'replace').decode())
|
||||
safeprint.safeprint(line)
|
||||
has_content = True
|
||||
else:
|
||||
os.rename(x, y)
|
||||
|
|
|
@ -10,6 +10,7 @@ import glob
|
|||
import re
|
||||
import sys
|
||||
|
||||
from voussoirkit import safeprint
|
||||
from voussoirkit import spinal
|
||||
|
||||
filepattern = sys.argv[1]
|
||||
|
@ -31,5 +32,5 @@ for filename in spinal.walk_generator():
|
|||
pass
|
||||
if matches:
|
||||
print(filename)
|
||||
print('\n'.join(matches).encode('ascii', 'replace').decode())
|
||||
safeprint.safeprint('\n'.join(matches))
|
||||
print()
|
||||
|
|
|
@ -5,14 +5,16 @@ import glob
|
|||
import os
|
||||
import sys
|
||||
|
||||
from voussoirkit import safeprint
|
||||
|
||||
def touch(glob_pattern):
|
||||
filenames = glob.glob(glob_pattern)
|
||||
if len(filenames) == 0:
|
||||
print(glob_pattern.encode('ascii', 'replace').decode())
|
||||
safeprint.safeprint(glob_pattern)
|
||||
open(glob_pattern, 'a').close()
|
||||
else:
|
||||
for filename in filenames:
|
||||
print(filename.encode('ascii', 'replace').decode())
|
||||
safeprint.safeprint(filename)
|
||||
os.utime(filename)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
BIN
VoxelSphereGenerator/dot_corner.png
Normal file
BIN
VoxelSphereGenerator/dot_corner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 168 B |
BIN
VoxelSphereGenerator/dot_highlight.png
Normal file
BIN
VoxelSphereGenerator/dot_highlight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 168 B |
BIN
VoxelSphereGenerator/dot_normal.png
Normal file
BIN
VoxelSphereGenerator/dot_normal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 168 B |
274
VoxelSphereGenerator/voxelspheregenerator.py
Normal file
274
VoxelSphereGenerator/voxelspheregenerator.py
Normal file
|
@ -0,0 +1,274 @@
|
|||
import argparse
|
||||
import math
|
||||
import PIL.Image
|
||||
import PIL.ImageDraw
|
||||
import sys
|
||||
|
||||
def choose_guideline_style(guideline_mod):
|
||||
if guideline_mod % 16 == 0:
|
||||
return ('#1f32ff', 3)
|
||||
if guideline_mod % 8 == 0:
|
||||
return ('#80f783', 2)
|
||||
if guideline_mod % 4 == 0:
|
||||
return ('#f4bffb', 1)
|
||||
|
||||
def voxelspheregenerator(WIDTH, HEIGH, DEPTH, WALL_THICKNESS=None):
|
||||
def in_ellipsoid(x, y, z, rad_x, rad_y, rad_z, center_x=None, center_y=None, center_z=None):
|
||||
'''
|
||||
Given a point (x, y, z), return whether that point lies inside the
|
||||
ellipsoid defined by (x/a)^2 + (y/b)^2 + (z/c)^2 = 1
|
||||
'''
|
||||
if center_x is None: center_x = rad_x
|
||||
if center_y is None: center_y = rad_y
|
||||
if center_z is None: center_z = rad_z
|
||||
#print(x, y, z, rad_x, rad_y, rad_z, center_x, center_y, center_z)
|
||||
x = ((x - center_x) / rad_x) ** 2
|
||||
y = ((y - center_y) / rad_y) ** 2
|
||||
z = ((z - center_z) / rad_z) ** 2
|
||||
distance = x + y + z
|
||||
#print(distance)
|
||||
return distance < 1
|
||||
|
||||
ODD_W = WIDTH % 2 == 1
|
||||
ODD_H = HEIGH % 2 == 1
|
||||
ODD_D = DEPTH % 2 == 1
|
||||
|
||||
RAD_X = WIDTH / 2
|
||||
RAD_Y = HEIGH / 2
|
||||
RAD_Z = DEPTH / 2
|
||||
|
||||
if WALL_THICKNESS:
|
||||
INNER_RAD_X = RAD_X - WALL_THICKNESS
|
||||
INNER_RAD_Y = RAD_Y - WALL_THICKNESS
|
||||
INNER_RAD_Z = RAD_Z - WALL_THICKNESS
|
||||
|
||||
X_CENTER = {WIDTH // 2} if ODD_W else {WIDTH // 2, (WIDTH // 2) - 1}
|
||||
Y_CENTER = {HEIGH // 2} if ODD_H else {HEIGH // 2, (HEIGH // 2) - 1}
|
||||
Z_CENTER = {DEPTH // 2} if ODD_D else {DEPTH // 2, (DEPTH // 2) - 1}
|
||||
|
||||
layer_digits = len(str(DEPTH))
|
||||
filename_form = '{w}x{h}x{d}w{wall}-{{layer:0{digits}}}.png'
|
||||
filename_form = filename_form.format(
|
||||
w=WIDTH,
|
||||
h=HEIGH,
|
||||
d=DEPTH,
|
||||
wall=WALL_THICKNESS if WALL_THICKNESS else 0,
|
||||
digits=layer_digits,
|
||||
)
|
||||
|
||||
dot_highlight = PIL.Image.open('dot_highlight.png')
|
||||
dot_normal = PIL.Image.open('dot_normal.png')
|
||||
dot_corner = PIL.Image.open('dot_corner.png')
|
||||
pixel_scale = dot_highlight.size[0]
|
||||
|
||||
# Space between each pixel
|
||||
PIXEL_MARGIN = 7
|
||||
|
||||
# Space between the pixel area and the canvas
|
||||
PIXELSPACE_MARGIN = 20
|
||||
|
||||
# Space between the canvas area and the image edge
|
||||
CANVAS_MARGIN = 20
|
||||
|
||||
LABEL_HEIGH = 20
|
||||
FINAL_IMAGE_SCALE = 1
|
||||
|
||||
PIXELSPACE_WIDTH = (WIDTH * pixel_scale) + ((WIDTH - 1) * PIXEL_MARGIN)
|
||||
PIXELSPACE_HEIGH = (HEIGH * pixel_scale) + ((HEIGH - 1) * PIXEL_MARGIN)
|
||||
|
||||
CANVAS_WIDTH = PIXELSPACE_WIDTH + (2 * PIXELSPACE_MARGIN)
|
||||
CANVAS_HEIGH = PIXELSPACE_HEIGH + (2 * PIXELSPACE_MARGIN)
|
||||
|
||||
IMAGE_WIDTH = CANVAS_WIDTH + (2 * CANVAS_MARGIN)
|
||||
IMAGE_HEIGH = CANVAS_HEIGH + (2 * CANVAS_MARGIN) + LABEL_HEIGH
|
||||
|
||||
CANVAS_START_X = CANVAS_MARGIN
|
||||
CANVAS_START_Y = CANVAS_MARGIN
|
||||
CANVAS_END_X = CANVAS_START_X + CANVAS_WIDTH
|
||||
CANVAS_END_Y = CANVAS_START_Y + CANVAS_HEIGH
|
||||
|
||||
PIXELSPACE_START_X = CANVAS_START_X + PIXELSPACE_MARGIN
|
||||
PIXELSPACE_START_Y = CANVAS_START_Y + PIXELSPACE_MARGIN
|
||||
PIXELSPACE_END_X = PIXELSPACE_START_X + PIXELSPACE_WIDTH
|
||||
PIXELSPACE_END_Y = PIXELSPACE_START_Y + PIXELSPACE_HEIGH
|
||||
|
||||
GUIDELINE_MOD_X = math.ceil(RAD_X)
|
||||
GUIDELINE_MOD_Y = math.ceil(RAD_Y)
|
||||
|
||||
def pixel_coord(x, y):
|
||||
x = PIXELSPACE_START_X + (x * pixel_scale) + (x * PIXEL_MARGIN)
|
||||
y = PIXELSPACE_START_Y + (y * pixel_scale) + (y * PIXEL_MARGIN)
|
||||
return (x, y)
|
||||
|
||||
def make_layer_matrix(z):
|
||||
layer_matrix = [[None for y in range(math.ceil(RAD_Y))] for x in range(math.ceil(RAD_X))]
|
||||
|
||||
# Generate the upper left corner.
|
||||
furthest_x = RAD_X
|
||||
furthest_y = RAD_Y
|
||||
for y in range(math.ceil(RAD_Y)):
|
||||
for x in range(math.ceil(RAD_X)):
|
||||
ux = x + 0.5
|
||||
uy = y + 0.5
|
||||
uz = z + 0.5
|
||||
|
||||
within = in_ellipsoid(ux, uy, uz, RAD_X, RAD_Y, RAD_Z)
|
||||
if WALL_THICKNESS:
|
||||
in_hole = in_ellipsoid(
|
||||
ux, uy, uz,
|
||||
INNER_RAD_X, INNER_RAD_Y, INNER_RAD_Z,
|
||||
RAD_X, RAD_Y, RAD_Z
|
||||
)
|
||||
within = within and not in_hole
|
||||
if within:
|
||||
if x in X_CENTER or y in Y_CENTER:
|
||||
if z in Z_CENTER:
|
||||
dot = dot_normal
|
||||
else:
|
||||
dot = dot_highlight
|
||||
else:
|
||||
if z in Z_CENTER:
|
||||
dot = dot_highlight
|
||||
else:
|
||||
dot = dot_normal
|
||||
layer_matrix[x][y] = dot
|
||||
furthest_x = min(x, furthest_x)
|
||||
furthest_y = min(y, furthest_y)
|
||||
#layer_image.paste(dot, box=(pixel_coord_x, pixel_coord_y))
|
||||
|
||||
# Mark the corner pieces
|
||||
for y in range(furthest_y, math.ceil(RAD_Y-1)):
|
||||
for x in range(furthest_x, math.ceil(RAD_X-1)):
|
||||
is_corner = (
|
||||
layer_matrix[x][y] is not None and
|
||||
layer_matrix[x-1][y+1] is not None and
|
||||
layer_matrix[x+1][y-1] is not None and
|
||||
(
|
||||
# Outer corners
|
||||
(layer_matrix[x][y-1] is None and layer_matrix[x-1][y] is None) or
|
||||
# Inner corners, if hollow
|
||||
(layer_matrix[x][y+1] is None and layer_matrix[x+1][y] is None)
|
||||
)
|
||||
)
|
||||
if is_corner:
|
||||
layer_matrix[x][y] = dot_corner
|
||||
|
||||
return layer_matrix
|
||||
|
||||
def make_layer_image(layer_matrix):
|
||||
layer_image = PIL.Image.new('RGBA', size=(IMAGE_WIDTH, IMAGE_HEIGH), color=(0, 0, 0, 0))
|
||||
draw = PIL.ImageDraw.ImageDraw(layer_image)
|
||||
|
||||
# Plot.
|
||||
for y in range(math.ceil(RAD_Y)):
|
||||
for x in range(math.ceil(RAD_X)):
|
||||
right_x = (WIDTH - 1) - x
|
||||
bottom_y = (HEIGH - 1) - y
|
||||
if layer_matrix[x][y] is not None:
|
||||
layer_image.paste(layer_matrix[x][y], box=pixel_coord(x, y))
|
||||
layer_image.paste(layer_matrix[x][y], box=pixel_coord(right_x, y))
|
||||
layer_image.paste(layer_matrix[x][y], box=pixel_coord(x, bottom_y))
|
||||
layer_image.paste(layer_matrix[x][y], box=pixel_coord(right_x, bottom_y))
|
||||
|
||||
# To draw the guidelines, start from
|
||||
for x in range(GUIDELINE_MOD_X % 4, WIDTH + 4, 4):
|
||||
# Vertical guideline
|
||||
as_if = GUIDELINE_MOD_X - x
|
||||
#print(x, as_if)
|
||||
line_x = PIXELSPACE_START_X + (x * pixel_scale) + (x * PIXEL_MARGIN)
|
||||
line_x = line_x - PIXEL_MARGIN + (PIXEL_MARGIN // 2)
|
||||
if line_x >= PIXELSPACE_END_X:
|
||||
continue
|
||||
(color, width) = choose_guideline_style(as_if)
|
||||
draw.line((line_x, CANVAS_START_Y, line_x, CANVAS_END_Y - 1), fill=color, width=width)
|
||||
draw.text((line_x, CANVAS_END_X), str(x), fill='#000')
|
||||
|
||||
for y in range(GUIDELINE_MOD_Y % 4, HEIGH + 4, 4):
|
||||
# Horizontal guideline
|
||||
as_if = GUIDELINE_MOD_Y - y
|
||||
#print(y, as_if)
|
||||
line_y = PIXELSPACE_START_Y + (y * pixel_scale) + (y * PIXEL_MARGIN)
|
||||
line_y = line_y - PIXEL_MARGIN + (PIXEL_MARGIN // 2)
|
||||
if line_y >= PIXELSPACE_END_Y:
|
||||
continue
|
||||
(color, width) = choose_guideline_style(as_if)
|
||||
draw.line((CANVAS_START_X, line_y, CANVAS_END_X - 1, line_y), fill=color, width=width)
|
||||
draw.text((CANVAS_END_X, line_y), str(y), fill='#000')
|
||||
|
||||
draw.rectangle((CANVAS_START_X, CANVAS_START_Y, CANVAS_END_X - 1, CANVAS_END_Y - 1), outline='#000')
|
||||
draw.text((CANVAS_START_X, IMAGE_HEIGH - LABEL_HEIGH), layer_filename, fill='#000')
|
||||
print(layer_filename)
|
||||
if FINAL_IMAGE_SCALE != 1:
|
||||
layer_image = layer_image.resize((FINAL_IMAGE_SCALE * IMAGE_WIDTH, FINAL_IMAGE_SCALE * IMAGE_HEIGH))
|
||||
|
||||
return layer_image
|
||||
|
||||
layer_matrices = []
|
||||
for z in range(DEPTH):
|
||||
if z < math.ceil(RAD_Z):
|
||||
layer_matrix = make_layer_matrix(z)
|
||||
layer_matrices.append(layer_matrix)
|
||||
else:
|
||||
layer_matrix = layer_matrices[(DEPTH - 1) - z]
|
||||
layer_filename = filename_form.format(layer=z)
|
||||
layer_image = make_layer_image(layer_matrix)
|
||||
layer_image.save(layer_filename)
|
||||
|
||||
|
||||
# Copy to the upper right corner.
|
||||
#for y in range(math.ceil(RAD_Y)):
|
||||
# #print(y)
|
||||
# for x in range(math.ceil(RAD_X), WIDTH):
|
||||
# #print(x, '==', (WIDTH-1) - x)
|
||||
# mapped_x = (WIDTH - 1) - x
|
||||
# layer_matrix[x][y] = layer_matrix[mapped_x][y]
|
||||
|
||||
# Copy to the lower semicircle.
|
||||
#for y in range(math.ceil(RAD_Y), HEIGH):
|
||||
# #print(y)
|
||||
# for x in range(WIDTH):
|
||||
# #print(y, '==', (HEIGH-1) - y)
|
||||
# mapped_y = (HEIGH-1) - y
|
||||
# layer_matrix[x][y] = layer_matrix[x][mapped_y]
|
||||
|
||||
|
||||
|
||||
#break
|
||||
#layer_matrix = [['▓' if dot == dot_highlight else '░' if dot == dot_normal else ' 'for dot in sublist] for sublist in layer_matrix]
|
||||
#layer_matrix = [''.join(sublist) for sublist in layer_matrix]
|
||||
#layer_matrix = '\n'.join(layer_matrix)
|
||||
#print(layer_matrix)
|
||||
#print()
|
||||
|
||||
|
||||
def voxelsphere_argparse(args):
|
||||
height_depth_match = bool(args.height) == bool(args.depth)
|
||||
if not height_depth_match:
|
||||
raise ValueError('Must provide both or neither of height+depth. Not just one.')
|
||||
|
||||
if (args.height is args.depth is None):
|
||||
args.height = args.width
|
||||
args.depth = args.width
|
||||
|
||||
voxelspheregenerator(
|
||||
int(args.width),
|
||||
int(args.height),
|
||||
int(args.depth),
|
||||
WALL_THICKNESS=int(args.wall_thickness) if args.wall_thickness else None,
|
||||
)
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('width')
|
||||
parser.add_argument('height', nargs='?', default=None)
|
||||
parser.add_argument('depth', nargs='?', default=None)
|
||||
parser.add_argument('--wall', dest='wall_thickness', default=None)
|
||||
parser.set_defaults(func=voxelsphere_argparse)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
args.func(args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
3
WebstreamZip/README.md
Normal file
3
WebstreamZip/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
PLEASE DON'T USE THIS
|
||||
|
||||
Use [python-zipstream](https://github.com/allanlei/python-zipstream) instead.
|
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
import webstreamzip
|
||||
|
||||
g = webstreamzip.stream_zip({'C:\\git\\else\\readme.md':'michael.md'})
|
||||
|
||||
x = open('test.zip', 'wb')
|
||||
for chunk in g:
|
||||
x.write(chunk)
|
|
@ -10,6 +10,7 @@ PATHS = [
|
|||
'C:\\git\\else\\Pathclass\\pathclass.py',
|
||||
'C:\\git\\else\\Ratelimiter\\ratelimiter.py',
|
||||
'C:\\git\\else\\RateMeter\\ratemeter.py',
|
||||
'C:\\git\\else\\Safeprint\\safeprint.py',
|
||||
'C:\\git\\else\\SpinalTap\\spinal.py',
|
||||
'C:\\git\\else\\WebstreamZip\\webstreamzip.py',
|
||||
]
|
||||
|
@ -40,7 +41,7 @@ import setuptools
|
|||
setuptools.setup(
|
||||
author='voussoir',
|
||||
name='{package}',
|
||||
version='0.0.3',
|
||||
version='0.0.4',
|
||||
description='',
|
||||
py_modules=[{py_modules}],
|
||||
)
|
||||
|
|
Binary file not shown.
Loading…
Reference in a new issue