Skip to content

Commit

Permalink
theme temperature
Browse files Browse the repository at this point in the history
  • Loading branch information
gywn committed Nov 27, 2017
1 parent 201c5a6 commit d0372dc
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 133 deletions.
126 changes: 0 additions & 126 deletions python/__init__.py
Original file line number Diff line number Diff line change
@@ -1,128 +1,2 @@
from argparse import ArgumentParser
import getpass
import os
from .native import LABtoRGB, LABtoLCH, LCHtoLAB, maxChroma, perception
from .version import __version__
from .pystache_tree import recursive_render


def hex(x):
return '{:02x}'.format(min(round(256 * max(0, x)), 255)).upper()


def rgb_hex(rgb):
return ''.join([hex(x) for x in rgb])


def interpolate2D(x1, y1, z1, x2, y2, z2, x3, y3, z3):
d = x2 * y1 - x3 * y1 - x1 * y2 + x3 * y2 + x1 * y3 - x2 * y3
c = x3 * y2 * z1 - x2 * y3 * z1 - x3 * y1 * z2 + x1 * y3 * z2 + x2 * y1 * z3 - x1 * y2 * z3
b = x3 * z1 + x1 * z2 - x3 * z2 - x1 * z3 + x2 * z3 - x2 * z1
a = y2 * z1 - y3 * z1 - y1 * z2 + y3 * z2 + y1 * z3 - y2 * z3
return lambda x, y: (c - y * b - x * a) / d


def argparser():
ps = ArgumentParser()

ps.add_argument(
'-n',
'--name',
type=str,
default='default',
help='theme name, default: \'default\'')
ps.add_argument(
'-f',
'--foreground',
type=float,
default=-1,
help='foreground luminocity between 0 and 100')
ps.add_argument(
'-b',
'--background',
type=float,
default=-1,
help='background luminocity between 0 and 100')
ps.add_argument(
'-L',
type=float,
default=-1,
help='main palette luminocity between 0 and 100')
ps.add_argument(
'--maxC',
type=float,
default=-1,
help=
'maximal chroma for main-palette, negative value for unconstrainted')
ps.add_argument(
'--sub-maxC',
type=float,
default=20,
help='maximal chroma for sub-palette, negative value for unconstrainted'
)
ps.add_argument('--mu', type=float, default=1.5, help='mu')
ps.add_argument('-D', '--debug', action='store_true', default=False)

return ps


def main():
args = argparser().parse_args()
if args.background < 0:
if args.foreground < 0:
args.foreground = 93
args.background = 100 - args.foreground
elif args.foreground < 0:
args.foreground = 100 - args.background
if (args.debug):
from pprint import pprint
pprint(args)
# L3: text color, very close to foreground
L3 = interpolate2D(0, 100, 45, 100, 0, 80, 60, 60, 60)(
args.foreground, args.background) if args.L < 0 else args.L
# L2: UI line/background, large inter-distance, close to foreground
L2 = interpolate2D(0, 100, 50, 100, 0, 70, 60, 60, 60)(args.foreground,
args.background)
# L1: indicator color, large inter-distance, close to background
L1 = interpolate2D(0, 100, 70, 100, 0, 55, 60, 60, 60)(args.foreground,
args.background)
# L0: background color, very close to background
L0 = interpolate2D(0, 100, 90, 100, 0, 15, 60, 60, 60)(args.foreground,
args.background)

semantic_hue = {'red': 30, 'yellow': 80, 'green': 120, 'blue': 260}
palette_main = perception(
7, L=L3, maxC=args.maxC, fixed=[(L3, 0, 0)], quiet=not args.debug)
context = {
'name': args.name,
'version': __version__,
'user': getpass.getuser(),
'ui-theme': 'vs-dark' if args.background < 50 else 'vs'
}

sgn = 1 if args.foreground > args.background else -1
context['fg-hex'] = rgb_hex(LABtoRGB((args.foreground, 0, 0)))
for i, delta in enumerate([0, 5, 12]):
context[f'bg-{i}-hex'] = rgb_hex(
LABtoRGB((args.background + sgn * delta, 0, 0)))
for i, delta in enumerate([15, 25, 60]):
context[f'line-{i}-hex'] = rgb_hex(
LABtoRGB((args.background + sgn * delta, 0, 0)))
for i, rgb in enumerate(palette_main['rgb']):
context[f'main-{i}-hex'] = rgb_hex(rgb)
for name, hue in semantic_hue.items():
for level, L in [(0, L0), (1, L1), (2, L2)]:
context[f'{name}-{level}-hex'] = rgb_hex(
LABtoRGB(LCHtoLAB(maxChroma([L, 0, hue], maxC=args.maxC))))

if (args.debug):
from pprint import pprint
palette_main["fitness"] = palette_main["fitness"][0]
# palette_sub["fitness"] = palette_sub["fitness"][0];
pprint(palette_main)
# pprint(palette_sub)
pprint(context)

recursive_render(
os.path.join(os.path.dirname(__file__), 'data/vscode'),
os.path.expanduser('~/.vscode/extensions'), context)
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@
// Quick picker
// "pickerGroup.border": "#f00",
"pickerGroup.foreground": "#{{green-2-hex}}",
"pickerGroup.foreground": "#{{blue-2-hex}}",
// Integrated terminal colors
"terminal.background": "#{{bg-0-hex}}",
Expand Down
14 changes: 9 additions & 5 deletions python/pystache_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@
def recursive_render(src_path,
dest_path,
context,
renderer=Renderer(missing_tags='strict')):
renderer=Renderer(missing_tags='strict'),
debug=True):
if (os.path.isdir(src_path)):
os.makedirs(dest_path, exist_ok=True)
print(f'Enter directory {dest_path}')
if debug:
print(f'Enter directory {dest_path}')
for name in os.listdir(src_path):
child_src_path = os.path.join(src_path, name)
child_dest_path = os.path.join(dest_path,
renderer.render(name, context))
recursive_render(child_src_path, child_dest_path, context,
renderer)
renderer, debug)
elif dest_path[-TEMPLATE_SUFFIX_LEN:] == TEMPLATE_SUFFIX:
dest_path = dest_path[:-TEMPLATE_SUFFIX_LEN]
with open(src_path, 'r') as src, open(dest_path, 'w') as dest:
dest.write(renderer.render(src.read(), context))
print(f'Parse file {dest_path}')
if debug:
print(f'Parse file {dest_path}')
else:
shutil.copyfile(src_path, dest_path)
print(f'Copy file {dest_path}')
if debug:
print(f'Copy file {dest_path}')
164 changes: 164 additions & 0 deletions python/theme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from argparse import ArgumentParser
from getpass import getuser
from itertools import product
import os
from .native import LABtoRGB, LCHtoLAB, maxChroma, perception
from .version import __version__
from .pystache_tree import recursive_render

D65_A = -1.65 / 100 # a component of D65 in LAB
D65_B = -19.33 / 100 # b component of D65 in LAB
SEMANTIC_HUE = {'red': 30, 'yellow': 80, 'green': 120, 'blue': 260}


def hex(x):
return '{:02x}'.format(min(round(256 * max(0, x)), 255)).upper()


def rgb_hex(rgb):
return ''.join([hex(x) for x in rgb])


def interpolate2D(x1, y1, z1, x2, y2, z2, x3, y3, z3):
d = x2 * y1 - x3 * y1 - x1 * y2 + x3 * y2 + x1 * y3 - x2 * y3
c = x3 * y2 * z1 - x2 * y3 * z1 - x3 * y1 * z2 + x1 * y3 * z2 + x2 * y1 * z3 - x1 * y2 * z3
b = x3 * z1 + x1 * z2 - x3 * z2 - x1 * z3 + x2 * z3 - x2 * z1
a = y2 * z1 - y3 * z1 - y1 * z2 + y3 * z2 + y1 * z3 - y2 * z3
return lambda x, y: (c - y * b - x * a) / d


def tempered_gray(L, dK):
return (L, L * D65_A * dK, L * D65_B * dK)


def argparser():
ps = ArgumentParser()

ps.add_argument(
'-n',
'--name',
default='default',
type=str,
help='theme name, default: \'default\'')
ps.add_argument(
'-f',
'--foreground',
default=-1,
dest='Lf',
metavar='Lf',
type=float,
help='foreground luminocity between 0 < Lf < 100')
ps.add_argument(
'-b',
'--background',
default=-1,
dest='Lb',
metavar='Lb',
type=float,
help='background luminocity 0 < Lb < 100')
ps.add_argument(
'-L',
default=-1,
type=float,
help='main palette luminocity 0 < L < 100')
ps.add_argument(
'--maxC',
default=-1,
dest='maxC',
type=float,
help='main palette maximal chroma > 0')
ps.add_argument(
'--temp',
default=0,
dest='dK',
metavar='dK',
type=float,
help='temperature hint, -1(warm) < dK < 1(cold)')
ps.add_argument(
'-i',
'--in-place',
action='store_true',
default=False,
dest='inplace',
help='update themes in place')
ps.add_argument('-v', '--verbose', action='store_true', default=False)

return ps


def parse(args, ctx):
data_dir = os.path.join(os.path.dirname(__file__), 'data')
if args.inplace:
templates = [('vscode', os.path.expanduser('~/.vscode/extensions'))]
for profile, dest in templates:
print(f'Apply profile \'{profile}\'')
recursive_render(
os.path.join(data_dir, profile), dest, ctx, debug=args.verbose)
else:
dest = os.path.abspath(f'./perception-themes-{args.name}')
print(f'Generate themes in {dest}')
recursive_render(data_dir, dest, ctx, debug=args.verbose)


def main():
args = argparser().parse_args()

if args.Lb < 0:
if args.Lf < 0:
args.Lf = 93
args.Lb = 100 - args.Lf
elif args.Lf < 0:
args.Lf = 100 - args.Lb

# L3: text color, very close to Lf
L3 = (interpolate2D(0, 100, 45, 100, 0, 80, 60, 60, 60)(args.Lf, args.Lb)
if args.L < 0 else args.L)
# L2: UI line/background, large inter-distance, close to Lf
L2 = interpolate2D(0, 100, 50, 100, 0, 70, 60, 60, 60)(args.Lf, args.Lb)
# L1: indicator color, large inter-distance, close to Lb
L1 = interpolate2D(0, 100, 70, 100, 0, 55, 60, 60, 60)(args.Lf, args.Lb)
# L0: background color, very close to Lb
L0 = interpolate2D(0, 100, 90, 100, 0, 15, 60, 60, 60)(args.Lf, args.Lb)

if (args.verbose):
print(f'Init parameters: Lf={args.Lf} L={L3} L2={L2} L1={L1} ' +
f'L0={L0} Lb={args.Lb} dK={args.dK}')
else:
print(
f'Init parameters: Lf={args.Lf} L={L3} Lb={args.Lb} dK={args.dK}')

sgn = 1 if args.Lf > args.Lb else -1
palette3 = perception(
7,
L=L3,
maxC=args.maxC,
fixed=[tempered_gray(L3, args.dK)],
quiet=not args.verbose)
ctx = {
'name': args.name,
'version': __version__,
'user': getuser(),
'ui-theme': 'vs-dark' if args.Lb < 50 else 'vs',
'fg-hex': rgb_hex(LABtoRGB((tempered_gray(args.Lf, args.dK))))
}

for i, rgb in enumerate(palette3['rgb']):
ctx[f'main-{i}-hex'] = rgb_hex(rgb)

for (name, hue), (level, L) in product(SEMANTIC_HUE.items(),
enumerate([L0, L1, L2])):
ctx[f'{name}-{level}-hex'] = rgb_hex(
LABtoRGB(LCHtoLAB(maxChroma([L, 0, hue], maxC=args.maxC))))

for i, delta in enumerate([15, 25, 60]):
ctx[f'line-{i}-hex'] = rgb_hex(
LABtoRGB(tempered_gray(args.Lb + sgn * delta, args.dK)))

for i, delta in enumerate([0, 5, 12]):
ctx[f'bg-{i}-hex'] = rgb_hex(
LABtoRGB(tempered_gray(args.Lb + sgn * delta, args.dK)))

print('Generated palette: ' + ' '.join(
[f'#{rgb_hex(rgb)}' for rgb in palette3["rgb"]]))

parse(args, ctx)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def build_extension(self, ext):
},
entry_points={
'console_scripts': [
'perception-theme = perception:main',
'perception-theme = perception.theme:main',
]
},
cmdclass=dict(build_ext=CMakeBuild))

0 comments on commit d0372dc

Please sign in to comment.