lutopia 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lutopia-0.2.0/PKG-INFO +223 -0
- lutopia-0.2.0/README.md +214 -0
- lutopia-0.2.0/pyproject.toml +52 -0
- lutopia-0.2.0/src/lutopia/__init__.py +41 -0
- lutopia-0.2.0/src/lutopia/core.py +67 -0
- lutopia-0.2.0/src/lutopia/palettes/accent.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/afmhot.json +46 -0
- lutopia-0.2.0/src/lutopia/palettes/autumn.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/berlin.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/bgy.json +254 -0
- lutopia-0.2.0/src/lutopia/palettes/bgyw.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/binary.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/bjy.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/bkr.json +134 -0
- lutopia-0.2.0/src/lutopia/palettes/bky.json +134 -0
- lutopia-0.2.0/src/lutopia/palettes/blues.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/bmw.json +238 -0
- lutopia-0.2.0/src/lutopia/palettes/bmy.json +230 -0
- lutopia-0.2.0/src/lutopia/palettes/bone.json +38 -0
- lutopia-0.2.0/src/lutopia/palettes/brbg.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/brg.json +38 -0
- lutopia-0.2.0/src/lutopia/palettes/bugn.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/bupu.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/bwr.json +38 -0
- lutopia-0.2.0/src/lutopia/palettes/bwy.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/c1.json +398 -0
- lutopia-0.2.0/src/lutopia/palettes/c10.json +190 -0
- lutopia-0.2.0/src/lutopia/palettes/c10s.json +198 -0
- lutopia-0.2.0/src/lutopia/palettes/c11.json +358 -0
- lutopia-0.2.0/src/lutopia/palettes/c11s.json +358 -0
- lutopia-0.2.0/src/lutopia/palettes/c1s.json +342 -0
- lutopia-0.2.0/src/lutopia/palettes/c2.json +382 -0
- lutopia-0.2.0/src/lutopia/palettes/c2s.json +366 -0
- lutopia-0.2.0/src/lutopia/palettes/c3.json +302 -0
- lutopia-0.2.0/src/lutopia/palettes/c3s.json +310 -0
- lutopia-0.2.0/src/lutopia/palettes/c4.json +334 -0
- lutopia-0.2.0/src/lutopia/palettes/c4s.json +342 -0
- lutopia-0.2.0/src/lutopia/palettes/c5.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/c5s.json +134 -0
- lutopia-0.2.0/src/lutopia/palettes/c6.json +486 -0
- lutopia-0.2.0/src/lutopia/palettes/c6s.json +478 -0
- lutopia-0.2.0/src/lutopia/palettes/c7.json +390 -0
- lutopia-0.2.0/src/lutopia/palettes/c7s.json +390 -0
- lutopia-0.2.0/src/lutopia/palettes/c8.json +270 -0
- lutopia-0.2.0/src/lutopia/palettes/c8s.json +246 -0
- lutopia-0.2.0/src/lutopia/palettes/c9.json +262 -0
- lutopia-0.2.0/src/lutopia/palettes/c9s.json +254 -0
- lutopia-0.2.0/src/lutopia/palettes/cbc1.json +310 -0
- lutopia-0.2.0/src/lutopia/palettes/cbc2.json +310 -0
- lutopia-0.2.0/src/lutopia/palettes/cbd1.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/cbd2.json +126 -0
- lutopia-0.2.0/src/lutopia/palettes/cbl1.json +206 -0
- lutopia-0.2.0/src/lutopia/palettes/cbl2.json +302 -0
- lutopia-0.2.0/src/lutopia/palettes/cbl3.json +190 -0
- lutopia-0.2.0/src/lutopia/palettes/cbl4.json +174 -0
- lutopia-0.2.0/src/lutopia/palettes/cbtc1.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/cbtc2.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/cbtd1.json +110 -0
- lutopia-0.2.0/src/lutopia/palettes/cbtl1.json +318 -0
- lutopia-0.2.0/src/lutopia/palettes/cbtl2.json +230 -0
- lutopia-0.2.0/src/lutopia/palettes/cbtl3.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/cbtl4.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/circle_mgbm_67_c31.json +190 -0
- lutopia-0.2.0/src/lutopia/palettes/circle_mgbm_67_c31_s25.json +198 -0
- lutopia-0.2.0/src/lutopia/palettes/cividis.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/cmrmap.json +86 -0
- lutopia-0.2.0/src/lutopia/palettes/colorwheel.json +366 -0
- lutopia-0.2.0/src/lutopia/palettes/cool.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/coolwarm.json +158 -0
- lutopia-0.2.0/src/lutopia/palettes/copper.json +30 -0
- lutopia-0.2.0/src/lutopia/palettes/cubehelix.json +262 -0
- lutopia-0.2.0/src/lutopia/palettes/cwr.json +110 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_bgrmb_35_70_c75.json +358 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_bgrmb_35_70_c75_s25.json +358 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_grey_15_85_c0.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_grey_15_85_c0_s25.json +134 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_isoluminant.json +190 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_mrybm_35_75_c68.json +398 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_mrybm_35_75_c68_s25.json +342 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_mybm_20_100_c48.json +262 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_mybm_20_100_c48_s25.json +254 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_mygbm_30_95_c78.json +382 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_mygbm_30_95_c78_s25.json +366 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_mygbm_50_90_c46.json +270 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_mygbm_50_90_c46_s25.json +246 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_protanopic_deuteranopic_bwyk_16_96_c31.json +310 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_protanopic_deuteranopic_wywb_55_96_c33.json +310 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_rygcbmr_50_90_c64.json +486 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_rygcbmr_50_90_c64_s25.json +478 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_tritanopic_cwrk_40_100_c20.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_tritanopic_wrwc_70_100_c20.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_wrkbw_10_90_c43.json +302 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_wrkbw_10_90_c43_s25.json +310 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_wrwbw_40_90_c42.json +334 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_wrwbw_40_90_c42_s25.json +342 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_ymcgy_60_90_c67.json +390 -0
- lutopia-0.2.0/src/lutopia/palettes/cyclic_ymcgy_60_90_c67_s25.json +390 -0
- lutopia-0.2.0/src/lutopia/palettes/d1.json +198 -0
- lutopia-0.2.0/src/lutopia/palettes/d10.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/d11.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/d12.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/d13.json +278 -0
- lutopia-0.2.0/src/lutopia/palettes/d1a.json +262 -0
- lutopia-0.2.0/src/lutopia/palettes/d2.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/d3.json +158 -0
- lutopia-0.2.0/src/lutopia/palettes/d4.json +134 -0
- lutopia-0.2.0/src/lutopia/palettes/d6.json +134 -0
- lutopia-0.2.0/src/lutopia/palettes/d7.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/d8.json +182 -0
- lutopia-0.2.0/src/lutopia/palettes/d9.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/dark2.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/dimgray.json +54 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_bkr_55_10_c35.json +134 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_bky_60_10_c30.json +134 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_bwg_20_95_c41.json +278 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_bwr_20_95_c54.json +262 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_bwr_40_95_c42.json +198 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_bwr_55_98_c37.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_cwm_80_100_c22.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_gkr_60_10_c40.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_gwr_55_95_c38.json +158 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_gwv_55_95_c39.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_isoluminant_cjm_75_c23.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_isoluminant_cjm_75_c24.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_isoluminant_cjo_70_c25.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_linear_bjr_30_55_c53.json +182 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_linear_bjy_30_90_c45.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_linear_protanopic_deuteranopic_bjy_57_89_c34.json +126 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_protanopic_deuteranopic_bwy_60_95_c32.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_rainbow_bgymr_45_85_c67.json +350 -0
- lutopia-0.2.0/src/lutopia/palettes/diverging_tritanopic_cwr_75_98_c20.json +110 -0
- lutopia-0.2.0/src/lutopia/palettes/fire.json +11 -0
- lutopia-0.2.0/src/lutopia/palettes/flag.json +1958 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_earth.json +174 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_gray.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_grey.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_heat.json +46 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_ncar.json +262 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_rainbow.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_stern.json +86 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_yarg.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/gist_yerg.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_bw.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_bw_minc_20.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_bw_minc_20_hue_150_280.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_bw_minc_20_hue_330_100.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_bw_minc_20_maxl_70.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_bw_minc_20_minl_30.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_category10.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_cool.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_dark.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_hv.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_light.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/glasbey_warm.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/gnbu.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/gnuplot.json +246 -0
- lutopia-0.2.0/src/lutopia/palettes/gnuplot2.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/gouldian.json +254 -0
- lutopia-0.2.0/src/lutopia/palettes/gray.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/grays.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/greens.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/grey.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/greys.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/gwv.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/hot.json +38 -0
- lutopia-0.2.0/src/lutopia/palettes/hsv.json +118 -0
- lutopia-0.2.0/src/lutopia/palettes/i1.json +110 -0
- lutopia-0.2.0/src/lutopia/palettes/i2.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/i3.json +126 -0
- lutopia-0.2.0/src/lutopia/palettes/inferno.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/isolum.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/isoluminant_cgo_70_c39.json +110 -0
- lutopia-0.2.0/src/lutopia/palettes/isoluminant_cgo_80_c38.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/isoluminant_cm_70_c39.json +126 -0
- lutopia-0.2.0/src/lutopia/palettes/jet.json +126 -0
- lutopia-0.2.0/src/lutopia/palettes/kb.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/kbc.json +238 -0
- lutopia-0.2.0/src/lutopia/palettes/kbgyw.json +358 -0
- lutopia-0.2.0/src/lutopia/palettes/kg.json +54 -0
- lutopia-0.2.0/src/lutopia/palettes/kgy.json +94 -0
- lutopia-0.2.0/src/lutopia/palettes/kr.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/l1.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/l10.json +110 -0
- lutopia-0.2.0/src/lutopia/palettes/l11.json +94 -0
- lutopia-0.2.0/src/lutopia/palettes/l12.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/l13.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/l14.json +54 -0
- lutopia-0.2.0/src/lutopia/palettes/l15.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/l16.json +374 -0
- lutopia-0.2.0/src/lutopia/palettes/l17.json +190 -0
- lutopia-0.2.0/src/lutopia/palettes/l18.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/l19.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/l2.json +54 -0
- lutopia-0.2.0/src/lutopia/palettes/l20.json +254 -0
- lutopia-0.2.0/src/lutopia/palettes/l3.json +246 -0
- lutopia-0.2.0/src/lutopia/palettes/l4.json +166 -0
- lutopia-0.2.0/src/lutopia/palettes/l5.json +94 -0
- lutopia-0.2.0/src/lutopia/palettes/l6.json +238 -0
- lutopia-0.2.0/src/lutopia/palettes/l7.json +214 -0
- lutopia-0.2.0/src/lutopia/palettes/l8.json +206 -0
- lutopia-0.2.0/src/lutopia/palettes/l9.json +310 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_bgy_10_95_c74.json +254 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_bgyw_15_100_c67.json +294 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_bgyw_15_100_c68.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_bgyw_20_98_c66.json +310 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_blue_5_95_c73.json +238 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_blue_95_50_c20.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_bmw_5_95_c86.json +214 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_bmw_5_95_c89.json +238 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_bmy_10_95_c71.json +206 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_bmy_10_95_c78.json +230 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_gow_60_85_c27.json +110 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_gow_65_90_c35.json +94 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_green_5_95_c69.json +94 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_grey_0_100_c0.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_grey_10_95_c0.json +54 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kbc_5_95_c73.json +238 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kbgoy_20_95_c57.json +254 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kbgyw_10_98_c63.json +358 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kbgyw_5_98_c62.json +374 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kgy_5_95_c69.json +94 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kry_0_97_c73.json +166 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kry_5_95_c72.json +222 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kry_5_98_c75.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kryw_0_100_c71.json +246 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kryw_5_100_c64.json +214 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_kryw_5_100_c67.json +190 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_protanopic_deuteranopic_kbjyw_5_95_c25.json +206 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_protanopic_deuteranopic_kbw_5_95_c34.json +190 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_protanopic_deuteranopic_kbw_5_98_c40.json +302 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_protanopic_deuteranopic_kyw_5_95_c49.json +174 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_ternary_blue_0_44_c57.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_ternary_green_0_46_c42.json +54 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_ternary_red_0_50_c52.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_tritanopic_kcw_5_95_c22.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_tritanopic_krjcw_5_95_c24.json +230 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_tritanopic_krjcw_5_98_c46.json +318 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_tritanopic_krw_5_95_c46.json +150 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_wcmr_100_45_c42.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_worb_100_25_c53.json +190 -0
- lutopia-0.2.0/src/lutopia/palettes/linear_wyor_100_45_c55.json +142 -0
- lutopia-0.2.0/src/lutopia/palettes/magma.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/managua.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/nipy_spectral.json +238 -0
- lutopia-0.2.0/src/lutopia/palettes/ocean.json +38 -0
- lutopia-0.2.0/src/lutopia/palettes/okabe_ito.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/oranges.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/orrd.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/paired.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/pastel1.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/pastel2.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/pink.json +126 -0
- lutopia-0.2.0/src/lutopia/palettes/piyg.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/plasma.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/prgn.json +94 -0
- lutopia-0.2.0/src/lutopia/palettes/prism.json +1838 -0
- lutopia-0.2.0/src/lutopia/palettes/pubu.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/pubugn.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/puor.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/purd.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/purples.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/r1.json +318 -0
- lutopia-0.2.0/src/lutopia/palettes/r2.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/r3.json +350 -0
- lutopia-0.2.0/src/lutopia/palettes/r4.json +326 -0
- lutopia-0.2.0/src/lutopia/palettes/rainbow.json +198 -0
- lutopia-0.2.0/src/lutopia/palettes/rainbow4.json +326 -0
- lutopia-0.2.0/src/lutopia/palettes/rainbow_bgyr_10_90_c83.json +326 -0
- lutopia-0.2.0/src/lutopia/palettes/rainbow_bgyr_35_85_c72.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/rainbow_bgyr_35_85_c73.json +286 -0
- lutopia-0.2.0/src/lutopia/palettes/rainbow_bgyrm_35_85_c69.json +318 -0
- lutopia-0.2.0/src/lutopia/palettes/rainbow_bgyrm_35_85_c71.json +270 -0
- lutopia-0.2.0/src/lutopia/palettes/rdbu.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/rdgy.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/rdpu.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/rdylbu.json +94 -0
- lutopia-0.2.0/src/lutopia/palettes/rdylgn.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/reds.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/seismic.json +54 -0
- lutopia-0.2.0/src/lutopia/palettes/set1.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/set2.json +70 -0
- lutopia-0.2.0/src/lutopia/palettes/set3.json +102 -0
- lutopia-0.2.0/src/lutopia/palettes/spectral.json +110 -0
- lutopia-0.2.0/src/lutopia/palettes/spring.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/summer.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/tab10.json +86 -0
- lutopia-0.2.0/src/lutopia/palettes/tab20.json +166 -0
- lutopia-0.2.0/src/lutopia/palettes/tab20b.json +166 -0
- lutopia-0.2.0/src/lutopia/palettes/tab20c.json +166 -0
- lutopia-0.2.0/src/lutopia/palettes/terrain.json +86 -0
- lutopia-0.2.0/src/lutopia/palettes/turbo.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/twilight.json +502 -0
- lutopia-0.2.0/src/lutopia/palettes/twilight_shifted.json +534 -0
- lutopia-0.2.0/src/lutopia/palettes/vanimo.json +2054 -0
- lutopia-0.2.0/src/lutopia/palettes/viridis.json +15 -0
- lutopia-0.2.0/src/lutopia/palettes/winter.json +22 -0
- lutopia-0.2.0/src/lutopia/palettes/wistia.json +46 -0
- lutopia-0.2.0/src/lutopia/palettes/ylgn.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/ylgnbu.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/ylorbr.json +78 -0
- lutopia-0.2.0/src/lutopia/palettes/ylorrd.json +78 -0
- lutopia-0.2.0/src/lutopia/viewer.py +224 -0
lutopia-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: lutopia
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Tool-agnostic LUT and colormap management for Python
|
|
5
|
+
Requires-Dist: numpy>=1.24
|
|
6
|
+
Requires-Dist: scipy>=1.10
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# lutopia
|
|
11
|
+
|
|
12
|
+
**Tool-agnostic LUT and colormap management for Python.**
|
|
13
|
+
|
|
14
|
+
Lutopia treats every palette — smooth gradient or legacy 256-step table — as a single unified continuous function. One API, zero fragmentation across visualization ecosystems.
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import lutopia as lt
|
|
18
|
+
|
|
19
|
+
# Matplotlib
|
|
20
|
+
from matplotlib.colors import ListedColormap
|
|
21
|
+
cmap = ListedColormap(lt.viridis.plt)
|
|
22
|
+
|
|
23
|
+
# Seaborn
|
|
24
|
+
sns.heatmap(data, cmap=lt.viridis.sns)
|
|
25
|
+
|
|
26
|
+
# Plotly
|
|
27
|
+
fig.update_traces(colorscale=lt.viridis.plotly)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **297 palettes out of the box** — matplotlib's full catalog + the entire colorcet scientific collection
|
|
35
|
+
- **Unified engine** — smooth gradients and legacy step-function LUTs share one interface
|
|
36
|
+
- **Zero visualization dependencies** — runtime package requires only `numpy` and `scipy`
|
|
37
|
+
- **Native consumer outputs** — `.plt`, `.mpl`, `.sns`, `.plotly` return exactly what each library expects, no adapters needed
|
|
38
|
+
- **Interactive palette picker** — `uvx lutopia` opens a live GUI browser with one-click code generation
|
|
39
|
+
- **`src` layout, `uv_build` backend** — modern packaging, ships a clean wheel
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install lutopia
|
|
47
|
+
# or
|
|
48
|
+
uv add lutopia
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import lutopia as lt
|
|
57
|
+
|
|
58
|
+
# List all available palettes
|
|
59
|
+
print(dir(lt)) # ['accent', 'afmhot', 'autumn', ..., 'viridis', ...]
|
|
60
|
+
|
|
61
|
+
# Sample raw float values (N × 3 array, values in [0, 1])
|
|
62
|
+
arr = lt.viridis.sample(256) # numpy array, shape (256, 3)
|
|
63
|
+
|
|
64
|
+
# Get hex strings
|
|
65
|
+
hexes = lt.plasma.to_hex_list(64) # ['#0d0887', '#2d0594', ...]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Consumer Properties
|
|
71
|
+
|
|
72
|
+
Every palette exposes three ready-to-use consumer properties. No third-party imports are required by the library itself.
|
|
73
|
+
|
|
74
|
+
### Matplotlib
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from matplotlib.colors import ListedColormap
|
|
78
|
+
import matplotlib.pyplot as plt
|
|
79
|
+
import lutopia as lt
|
|
80
|
+
|
|
81
|
+
cmap = ListedColormap(lt.inferno.plt)
|
|
82
|
+
plt.imshow(data, cmap=cmap)
|
|
83
|
+
plt.colorbar()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`.plt` / `.mpl` / `.matplotlib` all return the same `(256, 3)` float64 NumPy array that `ListedColormap` and `LinearSegmentedColormap` accept directly.
|
|
87
|
+
|
|
88
|
+
### Seaborn
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import seaborn as sns
|
|
92
|
+
import lutopia as lt
|
|
93
|
+
|
|
94
|
+
sns.heatmap(data, cmap=lt.magma.sns)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`.sns` / `.seaborn` return a list of 256 hex strings, which Seaborn accepts for both `palette=` and `cmap=` arguments.
|
|
98
|
+
|
|
99
|
+
### Plotly
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
import plotly.graph_objects as go
|
|
103
|
+
import lutopia as lt
|
|
104
|
+
|
|
105
|
+
fig = go.Figure(go.Heatmap(z=data, colorscale=lt.turbo.plotly))
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`.plotly` returns a native Plotly colorscale — a list of `[fraction, "#rrggbb"]` pairs spanning `[0.0, 1.0]`.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Palette Browser
|
|
113
|
+
|
|
114
|
+
The interactive palette picker is available as an ephemeral tool — no install needed:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uvx lutopia
|
|
118
|
+
# or, if already installed:
|
|
119
|
+
lutopia
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A tkinter window opens showing all 297 palettes as color swatches. Click any palette to open a code snippet dialog:
|
|
123
|
+
|
|
124
|
+
- Choose your target ecosystem (Matplotlib, Seaborn, Plotly, or Raw Hex)
|
|
125
|
+
- Copy the generated snippet directly to your clipboard
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Available Palettes
|
|
130
|
+
|
|
131
|
+
Lutopia ships with palettes sourced from:
|
|
132
|
+
|
|
133
|
+
| Source | Count | Examples |
|
|
134
|
+
|--------|-------|---------|
|
|
135
|
+
| **Matplotlib** | ~90 | `viridis`, `plasma`, `inferno`, `magma`, `cividis`, `jet`, `hot`, `coolwarm`, `spectral`, `tab10` |
|
|
136
|
+
| **Colorcet** | ~200 | `fire`, `rainbow4`, `bmy`, `bgyw`, `isolum`, `gouldian`, `kbc`, `dimgray` |
|
|
137
|
+
|
|
138
|
+
All palettes are available immediately after install — no scraping, no network calls.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## How It Works
|
|
143
|
+
|
|
144
|
+
All palettes are stored as compact JSON anchor files in `lutopia/palettes/`. A continuous `UnifiedPalette` engine evaluates them on demand:
|
|
145
|
+
|
|
146
|
+
- **Linear interpolation** for smooth gradient maps (e.g. viridis, plasma)
|
|
147
|
+
- **Nearest-neighbor / step** for discrete legacy tables (e.g. ImageJ `.lut` files)
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"name": "viridis",
|
|
152
|
+
"interpolation": "linear",
|
|
153
|
+
"anchors": [
|
|
154
|
+
[0.0, [68, 1, 84]],
|
|
155
|
+
[0.5, [33, 145, 140]],
|
|
156
|
+
[1.0, [253, 231, 37]]
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Palettes are loaded lazily on first access and cached for the lifetime of the interpreter session.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Development
|
|
166
|
+
|
|
167
|
+
### Setup
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
git clone https://github.com/bryanbarcelona/lutopia.git
|
|
171
|
+
cd lutopia
|
|
172
|
+
uv sync
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Tooling
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
uv run ruff check . # lint
|
|
179
|
+
uv run ruff format . # format
|
|
180
|
+
uv run ty check src/lutopia # type check
|
|
181
|
+
uv run pytest # tests
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Adding Palettes
|
|
185
|
+
|
|
186
|
+
**From matplotlib / colorcet:**
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
uv run python scripts/scrape_ecosystems.py
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Reads upstream colormap libraries, reduces continuous maps to sparse anchor points (max error < 1/255), and writes JSON assets to `src/lutopia/palettes/`. Run with `--overwrite` to force regeneration.
|
|
193
|
+
|
|
194
|
+
**From a binary ImageJ / Fiji `.lut` file:**
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
uv run python scripts/parse_legacy_lut.py path/to/fire.lut [name]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Parses the standard 768-byte RGB block and writes a step-interpolation JSON asset.
|
|
201
|
+
|
|
202
|
+
After adding palettes, commit the new JSON files and they will be included in the next release.
|
|
203
|
+
|
|
204
|
+
### Releasing
|
|
205
|
+
|
|
206
|
+
Releases are fully automated via CI. Every merge to `main` triggers:
|
|
207
|
+
|
|
208
|
+
1. Commitizen bumps the version and generates a changelog entry
|
|
209
|
+
2. `uv build` produces the wheel and sdist
|
|
210
|
+
3. `uv publish` pushes to PyPI via trusted publishing (OIDC, no API keys)
|
|
211
|
+
4. A GitHub Release is created and tagged
|
|
212
|
+
|
|
213
|
+
Commit messages must follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
cz commit
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
MIT
|
lutopia-0.2.0/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# lutopia
|
|
2
|
+
|
|
3
|
+
**Tool-agnostic LUT and colormap management for Python.**
|
|
4
|
+
|
|
5
|
+
Lutopia treats every palette — smooth gradient or legacy 256-step table — as a single unified continuous function. One API, zero fragmentation across visualization ecosystems.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import lutopia as lt
|
|
9
|
+
|
|
10
|
+
# Matplotlib
|
|
11
|
+
from matplotlib.colors import ListedColormap
|
|
12
|
+
cmap = ListedColormap(lt.viridis.plt)
|
|
13
|
+
|
|
14
|
+
# Seaborn
|
|
15
|
+
sns.heatmap(data, cmap=lt.viridis.sns)
|
|
16
|
+
|
|
17
|
+
# Plotly
|
|
18
|
+
fig.update_traces(colorscale=lt.viridis.plotly)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- **297 palettes out of the box** — matplotlib's full catalog + the entire colorcet scientific collection
|
|
26
|
+
- **Unified engine** — smooth gradients and legacy step-function LUTs share one interface
|
|
27
|
+
- **Zero visualization dependencies** — runtime package requires only `numpy` and `scipy`
|
|
28
|
+
- **Native consumer outputs** — `.plt`, `.mpl`, `.sns`, `.plotly` return exactly what each library expects, no adapters needed
|
|
29
|
+
- **Interactive palette picker** — `uvx lutopia` opens a live GUI browser with one-click code generation
|
|
30
|
+
- **`src` layout, `uv_build` backend** — modern packaging, ships a clean wheel
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install lutopia
|
|
38
|
+
# or
|
|
39
|
+
uv add lutopia
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import lutopia as lt
|
|
48
|
+
|
|
49
|
+
# List all available palettes
|
|
50
|
+
print(dir(lt)) # ['accent', 'afmhot', 'autumn', ..., 'viridis', ...]
|
|
51
|
+
|
|
52
|
+
# Sample raw float values (N × 3 array, values in [0, 1])
|
|
53
|
+
arr = lt.viridis.sample(256) # numpy array, shape (256, 3)
|
|
54
|
+
|
|
55
|
+
# Get hex strings
|
|
56
|
+
hexes = lt.plasma.to_hex_list(64) # ['#0d0887', '#2d0594', ...]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Consumer Properties
|
|
62
|
+
|
|
63
|
+
Every palette exposes three ready-to-use consumer properties. No third-party imports are required by the library itself.
|
|
64
|
+
|
|
65
|
+
### Matplotlib
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from matplotlib.colors import ListedColormap
|
|
69
|
+
import matplotlib.pyplot as plt
|
|
70
|
+
import lutopia as lt
|
|
71
|
+
|
|
72
|
+
cmap = ListedColormap(lt.inferno.plt)
|
|
73
|
+
plt.imshow(data, cmap=cmap)
|
|
74
|
+
plt.colorbar()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`.plt` / `.mpl` / `.matplotlib` all return the same `(256, 3)` float64 NumPy array that `ListedColormap` and `LinearSegmentedColormap` accept directly.
|
|
78
|
+
|
|
79
|
+
### Seaborn
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import seaborn as sns
|
|
83
|
+
import lutopia as lt
|
|
84
|
+
|
|
85
|
+
sns.heatmap(data, cmap=lt.magma.sns)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`.sns` / `.seaborn` return a list of 256 hex strings, which Seaborn accepts for both `palette=` and `cmap=` arguments.
|
|
89
|
+
|
|
90
|
+
### Plotly
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import plotly.graph_objects as go
|
|
94
|
+
import lutopia as lt
|
|
95
|
+
|
|
96
|
+
fig = go.Figure(go.Heatmap(z=data, colorscale=lt.turbo.plotly))
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`.plotly` returns a native Plotly colorscale — a list of `[fraction, "#rrggbb"]` pairs spanning `[0.0, 1.0]`.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Palette Browser
|
|
104
|
+
|
|
105
|
+
The interactive palette picker is available as an ephemeral tool — no install needed:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uvx lutopia
|
|
109
|
+
# or, if already installed:
|
|
110
|
+
lutopia
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
A tkinter window opens showing all 297 palettes as color swatches. Click any palette to open a code snippet dialog:
|
|
114
|
+
|
|
115
|
+
- Choose your target ecosystem (Matplotlib, Seaborn, Plotly, or Raw Hex)
|
|
116
|
+
- Copy the generated snippet directly to your clipboard
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Available Palettes
|
|
121
|
+
|
|
122
|
+
Lutopia ships with palettes sourced from:
|
|
123
|
+
|
|
124
|
+
| Source | Count | Examples |
|
|
125
|
+
|--------|-------|---------|
|
|
126
|
+
| **Matplotlib** | ~90 | `viridis`, `plasma`, `inferno`, `magma`, `cividis`, `jet`, `hot`, `coolwarm`, `spectral`, `tab10` |
|
|
127
|
+
| **Colorcet** | ~200 | `fire`, `rainbow4`, `bmy`, `bgyw`, `isolum`, `gouldian`, `kbc`, `dimgray` |
|
|
128
|
+
|
|
129
|
+
All palettes are available immediately after install — no scraping, no network calls.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## How It Works
|
|
134
|
+
|
|
135
|
+
All palettes are stored as compact JSON anchor files in `lutopia/palettes/`. A continuous `UnifiedPalette` engine evaluates them on demand:
|
|
136
|
+
|
|
137
|
+
- **Linear interpolation** for smooth gradient maps (e.g. viridis, plasma)
|
|
138
|
+
- **Nearest-neighbor / step** for discrete legacy tables (e.g. ImageJ `.lut` files)
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"name": "viridis",
|
|
143
|
+
"interpolation": "linear",
|
|
144
|
+
"anchors": [
|
|
145
|
+
[0.0, [68, 1, 84]],
|
|
146
|
+
[0.5, [33, 145, 140]],
|
|
147
|
+
[1.0, [253, 231, 37]]
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Palettes are loaded lazily on first access and cached for the lifetime of the interpreter session.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Development
|
|
157
|
+
|
|
158
|
+
### Setup
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
git clone https://github.com/bryanbarcelona/lutopia.git
|
|
162
|
+
cd lutopia
|
|
163
|
+
uv sync
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Tooling
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
uv run ruff check . # lint
|
|
170
|
+
uv run ruff format . # format
|
|
171
|
+
uv run ty check src/lutopia # type check
|
|
172
|
+
uv run pytest # tests
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Adding Palettes
|
|
176
|
+
|
|
177
|
+
**From matplotlib / colorcet:**
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
uv run python scripts/scrape_ecosystems.py
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Reads upstream colormap libraries, reduces continuous maps to sparse anchor points (max error < 1/255), and writes JSON assets to `src/lutopia/palettes/`. Run with `--overwrite` to force regeneration.
|
|
184
|
+
|
|
185
|
+
**From a binary ImageJ / Fiji `.lut` file:**
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
uv run python scripts/parse_legacy_lut.py path/to/fire.lut [name]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Parses the standard 768-byte RGB block and writes a step-interpolation JSON asset.
|
|
192
|
+
|
|
193
|
+
After adding palettes, commit the new JSON files and they will be included in the next release.
|
|
194
|
+
|
|
195
|
+
### Releasing
|
|
196
|
+
|
|
197
|
+
Releases are fully automated via CI. Every merge to `main` triggers:
|
|
198
|
+
|
|
199
|
+
1. Commitizen bumps the version and generates a changelog entry
|
|
200
|
+
2. `uv build` produces the wheel and sdist
|
|
201
|
+
3. `uv publish` pushes to PyPI via trusted publishing (OIDC, no API keys)
|
|
202
|
+
4. A GitHub Release is created and tagged
|
|
203
|
+
|
|
204
|
+
Commit messages must follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
cz commit
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "lutopia"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Tool-agnostic LUT and colormap management for Python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"numpy>=1.24",
|
|
9
|
+
"scipy>=1.10",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[project.scripts]
|
|
13
|
+
lutopia = "lutopia.viewer:main"
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["uv_build>=0.9.22,<0.10.0"]
|
|
17
|
+
build-backend = "uv_build"
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"matplotlib>=3.7",
|
|
22
|
+
"seaborn>=0.12",
|
|
23
|
+
"colorcet>=3.0",
|
|
24
|
+
"pytest>=8.0",
|
|
25
|
+
"pytest-cov>=5.0",
|
|
26
|
+
"commitizen>=3.0",
|
|
27
|
+
"ruff>=0.4",
|
|
28
|
+
"ty>=0.0.0a1",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.pytest.ini_options]
|
|
32
|
+
testpaths = ["tests"]
|
|
33
|
+
|
|
34
|
+
[tool.ruff]
|
|
35
|
+
target-version = "py312"
|
|
36
|
+
line-length = 88
|
|
37
|
+
|
|
38
|
+
[tool.ruff.lint]
|
|
39
|
+
select = ["E", "F", "I", "N", "UP"]
|
|
40
|
+
|
|
41
|
+
[tool.ruff.lint.per-file-ignores]
|
|
42
|
+
"tests/*" = ["N"]
|
|
43
|
+
"scripts/*" = ["N"]
|
|
44
|
+
|
|
45
|
+
[tool.commitizen]
|
|
46
|
+
name = "cz_conventional_commits"
|
|
47
|
+
version_provider = "pep621"
|
|
48
|
+
tag_format = "v$version"
|
|
49
|
+
update_changelog_on_bump = true
|
|
50
|
+
|
|
51
|
+
[tool.ty.environment]
|
|
52
|
+
python-version = "3.12"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from lutopia.core import UnifiedPalette
|
|
9
|
+
|
|
10
|
+
_PALETTES_DIR = Path(__file__).parent / "palettes"
|
|
11
|
+
_cache: dict[str, UnifiedPalette] = {}
|
|
12
|
+
_names: list[str] | None = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _palette_names() -> list[str]:
|
|
16
|
+
global _names
|
|
17
|
+
if _names is None:
|
|
18
|
+
_names = sorted(p.stem for p in _PALETTES_DIR.glob("*.json"))
|
|
19
|
+
return _names
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def __getattr__(name: str) -> UnifiedPalette:
|
|
23
|
+
if name.startswith("_"):
|
|
24
|
+
raise AttributeError(name)
|
|
25
|
+
names = _palette_names()
|
|
26
|
+
if name not in names:
|
|
27
|
+
available = ", ".join(names[:8]) + ("..." if len(names) > 8 else "")
|
|
28
|
+
raise AttributeError(f"lutopia has no palette {name!r}. Available: {available}")
|
|
29
|
+
if name not in _cache:
|
|
30
|
+
from lutopia.core import UnifiedPalette
|
|
31
|
+
|
|
32
|
+
raw = (_PALETTES_DIR / f"{name}.json").read_bytes()
|
|
33
|
+
_cache[name] = UnifiedPalette(json.loads(raw))
|
|
34
|
+
return _cache[name]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def __dir__() -> list[str]:
|
|
38
|
+
return _palette_names()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
__all__ = _palette_names()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from scipy.interpolate import interp1d
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UnifiedPalette:
|
|
10
|
+
def __init__(self, json_data: dict[str, Any]) -> None:
|
|
11
|
+
self.name: str = json_data["name"]
|
|
12
|
+
self.mode: str = json_data["interpolation"] # "linear" or "step"
|
|
13
|
+
|
|
14
|
+
anchors: list[list] = json_data["anchors"]
|
|
15
|
+
self.domain: np.ndarray = np.array([a[0] for a in anchors], dtype=float)
|
|
16
|
+
self.colors: np.ndarray = np.array([a[1] for a in anchors], dtype=float) / 255.0
|
|
17
|
+
|
|
18
|
+
kind = "linear" if self.mode == "linear" else "nearest"
|
|
19
|
+
self._evaluator: interp1d = interp1d(
|
|
20
|
+
self.domain,
|
|
21
|
+
self.colors,
|
|
22
|
+
axis=0,
|
|
23
|
+
kind=kind,
|
|
24
|
+
bounds_error=False,
|
|
25
|
+
fill_value=(self.colors[0], self.colors[-1]),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def sample(self, steps: int = 256) -> np.ndarray:
|
|
29
|
+
"""Evaluate the continuous function at N evenly distributed steps."""
|
|
30
|
+
target_domain = np.linspace(self.domain[0], self.domain[-1], steps)
|
|
31
|
+
return np.clip(self._evaluator(target_domain), 0.0, 1.0)
|
|
32
|
+
|
|
33
|
+
def to_hex_list(self, steps: int = 256) -> list[str]:
|
|
34
|
+
"""Return hex color strings for N samples."""
|
|
35
|
+
rgb = np.round(self.sample(steps) * 255).astype(np.uint8)
|
|
36
|
+
return [f"#{r:02x}{g:02x}{b:02x}" for r, g, b in rgb]
|
|
37
|
+
|
|
38
|
+
# --- NATIVE CONSUMER SHAPING PROPERTIES (no third-party imports) ---
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def matplotlib(self) -> np.ndarray:
|
|
42
|
+
"""Normalized (256, 3) float array for ListedColormap."""
|
|
43
|
+
return self.sample(256)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def plt(self) -> np.ndarray:
|
|
47
|
+
return self.matplotlib
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def mpl(self) -> np.ndarray:
|
|
51
|
+
return self.matplotlib
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def seaborn(self) -> list[str]:
|
|
55
|
+
"""List of 256 hex strings for Seaborn palette/cmap signatures."""
|
|
56
|
+
return self.to_hex_list(256)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def sns(self) -> list[str]:
|
|
60
|
+
return self.seaborn
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def plotly(self) -> list[list]:
|
|
64
|
+
"""Plotly colorscale: [[fraction, hex], ...] over 256 steps."""
|
|
65
|
+
hex_list = self.to_hex_list(256)
|
|
66
|
+
n = len(hex_list)
|
|
67
|
+
return [[i / (n - 1), hex_list[i]] for i in range(n)]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "accent",
|
|
3
|
+
"interpolation": "step",
|
|
4
|
+
"anchors": [
|
|
5
|
+
[
|
|
6
|
+
0,
|
|
7
|
+
[
|
|
8
|
+
127,
|
|
9
|
+
201,
|
|
10
|
+
127
|
|
11
|
+
]
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
1,
|
|
15
|
+
[
|
|
16
|
+
127,
|
|
17
|
+
201,
|
|
18
|
+
127
|
|
19
|
+
]
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
2,
|
|
23
|
+
[
|
|
24
|
+
127,
|
|
25
|
+
201,
|
|
26
|
+
127
|
|
27
|
+
]
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
3,
|
|
31
|
+
[
|
|
32
|
+
127,
|
|
33
|
+
201,
|
|
34
|
+
127
|
|
35
|
+
]
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
4,
|
|
39
|
+
[
|
|
40
|
+
127,
|
|
41
|
+
201,
|
|
42
|
+
127
|
|
43
|
+
]
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
5,
|
|
47
|
+
[
|
|
48
|
+
127,
|
|
49
|
+
201,
|
|
50
|
+
127
|
|
51
|
+
]
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
6,
|
|
55
|
+
[
|
|
56
|
+
127,
|
|
57
|
+
201,
|
|
58
|
+
127
|
|
59
|
+
]
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
7,
|
|
63
|
+
[
|
|
64
|
+
127,
|
|
65
|
+
201,
|
|
66
|
+
127
|
|
67
|
+
]
|
|
68
|
+
]
|
|
69
|
+
]
|
|
70
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "afmhot",
|
|
3
|
+
"interpolation": "linear",
|
|
4
|
+
"anchors": [
|
|
5
|
+
[
|
|
6
|
+
0.0,
|
|
7
|
+
[
|
|
8
|
+
0,
|
|
9
|
+
0,
|
|
10
|
+
0
|
|
11
|
+
]
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
0.25098,
|
|
15
|
+
[
|
|
16
|
+
128,
|
|
17
|
+
0,
|
|
18
|
+
0
|
|
19
|
+
]
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
0.498039,
|
|
23
|
+
[
|
|
24
|
+
254,
|
|
25
|
+
126,
|
|
26
|
+
0
|
|
27
|
+
]
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
0.74902,
|
|
31
|
+
[
|
|
32
|
+
255,
|
|
33
|
+
254,
|
|
34
|
+
127
|
|
35
|
+
]
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
1.0,
|
|
39
|
+
[
|
|
40
|
+
255,
|
|
41
|
+
255,
|
|
42
|
+
255
|
|
43
|
+
]
|
|
44
|
+
]
|
|
45
|
+
]
|
|
46
|
+
}
|