init commit
This commit is contained in:
+328
@@ -0,0 +1,328 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,python,react
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,python,react
|
||||
|
||||
js/output.css
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
### Python Patch ###
|
||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||
poetry.toml
|
||||
|
||||
# ruff
|
||||
.ruff_cache/
|
||||
|
||||
# LSP config files
|
||||
pyrightconfig.json
|
||||
|
||||
### react ###
|
||||
.DS_*
|
||||
**/*.backup.*
|
||||
**/*.back.*
|
||||
|
||||
node_modules
|
||||
|
||||
*.sublime*
|
||||
|
||||
psd
|
||||
thumb
|
||||
sketch
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node,python,react
|
||||
@@ -0,0 +1,30 @@
|
||||
# avatar-graph-comfyui
|
||||
|
||||
This introduce a custom workflow on top of comfyui built for avatar-graph.
|
||||
|
||||
# Installation
|
||||
|
||||
1. `cd custom_nodes`
|
||||
|
||||
2. `git clone https://github.com/avatechgg/avatar-graph-comfyui.git`
|
||||
|
||||
3. Install deps `python -m pip install -r requirements.txt`
|
||||
|
||||
4. Navigate to in your comfyui installation folder, open `comfyui/main.py` and add the following lines:
|
||||
```py
|
||||
def prompt_worker(q, server):
|
||||
e = execution.PromptExecutor(server)
|
||||
|
||||
# Add this line here
|
||||
import sys
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "custom_nodes", "avatar-graph-comfyui"))
|
||||
import global_bpy
|
||||
|
||||
while True:
|
||||
item, item_id = q.get()
|
||||
# Add this line here
|
||||
global_bpy.reset_bpy()
|
||||
|
||||
```
|
||||
|
||||
4. Restart comfyui
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
@author: Avatech Limited
|
||||
@title: Avatar Graph
|
||||
@nickname: Avatar Graph
|
||||
@description: Include nodes for sam + bpy operation, that allows workflow creations for generative 2d character rig.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import importlib
|
||||
import subprocess
|
||||
|
||||
ag_path = os.path.join(os.path.dirname(__file__))
|
||||
|
||||
# pip_install = [sys.executable, '-m', 'pip', 'install']
|
||||
|
||||
# def ensure_pip_packages():
|
||||
# try:
|
||||
# # import bpy
|
||||
# import segment_anything
|
||||
# except Exception:
|
||||
# my_path = os.path.dirname(__file__)
|
||||
# requirements_path = os.path.join(my_path, "requirements.txt")
|
||||
# subprocess.check_call(pip_install + ['-r', requirements_path])
|
||||
|
||||
# ensure_pip_packages()
|
||||
|
||||
def get_python_files(path):
|
||||
return [f[:-3] for f in os.listdir(path) if f.endswith('.py')]
|
||||
|
||||
def append_to_sys_path(path):
|
||||
if path not in sys.path:
|
||||
sys.path.append(path)
|
||||
|
||||
paths = ['blender', 'sam', 'common']
|
||||
files = []
|
||||
|
||||
for path in paths:
|
||||
full_path = os.path.join(ag_path, path)
|
||||
append_to_sys_path(full_path)
|
||||
files.extend(get_python_files(full_path))
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
|
||||
# Import all the modules and append their mappings
|
||||
for file in files:
|
||||
module = importlib.import_module(file)
|
||||
if hasattr(module, 'NODE_CLASS_MAPPINGS'):
|
||||
NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS)
|
||||
if hasattr(module, 'NODE_DISPLAY_NAME_MAPPINGS'):
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
|
||||
WEB_DIRECTORY = "js"
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
@@ -0,0 +1,98 @@
|
||||
class ApplyMeshTransformAsShapeKey:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs": ("BPY_OBJS",),
|
||||
"shape_key_name": ("STRING", {
|
||||
# True if you want the field to look like the one on the ClipTextEncode node
|
||||
"multiline": False,
|
||||
"default": "EyeBlinkLeft", # default value
|
||||
}),
|
||||
"target_vertex_group": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "Group"
|
||||
}),
|
||||
"scale_x": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
"scale_y": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
"offset_x": ("FLOAT", {"default": 0.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
"offset_y": ("FLOAT", {"default": 0.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS", )
|
||||
RETURN_NAMES = ("bpy_objs", )
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
# OUTPUT_NODE = False
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs, shape_key_name, target_vertex_group, scale_x, scale_y, offset_x, offset_y):
|
||||
from mathutils import Vector
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
obj = bpy_objs[0]
|
||||
|
||||
# Create new shape key
|
||||
sk = obj.shape_key_add(name=shape_key_name)
|
||||
sk.interpolation = 'KEY_LINEAR'
|
||||
|
||||
verts = obj.data.vertices
|
||||
|
||||
# get vertex group
|
||||
vertex_group = obj.vertex_groups[target_vertex_group]
|
||||
|
||||
# get the verts in the group
|
||||
verts_in_group = [v for v in verts if vertex_group.index in [vg.group for vg in v.groups]]
|
||||
|
||||
# position each vert
|
||||
# for i in range(len(verts)):
|
||||
# Calculate center of mesh
|
||||
# center = sum((vert.co for vert in verts), Vector()) / len(verts)
|
||||
center = sum((vert.co for vert in verts_in_group), Vector()) / len(verts_in_group)
|
||||
# apply scale relative to object center
|
||||
|
||||
# Position each vert
|
||||
for i, vert in enumerate(verts):
|
||||
# return if vert is not in group
|
||||
if vertex_group.index not in [vg.group for vg in vert.groups]:
|
||||
continue
|
||||
# Get vector from center to vertex
|
||||
vec = vert.co - center
|
||||
|
||||
# Scale vector
|
||||
# if scale_x != 0:
|
||||
vec.x *= scale_x
|
||||
# if scale_x != 0:
|
||||
vec.y *= scale_y
|
||||
|
||||
# # Offset vector
|
||||
vec.x += offset_x
|
||||
vec.y += offset_y
|
||||
|
||||
# Calculate new position
|
||||
new_pos = center + vec
|
||||
|
||||
# Apply new position
|
||||
# vert.co = new_pos
|
||||
sk.data[i].co = new_pos
|
||||
|
||||
return (bpy_objs, )
|
||||
|
||||
|
||||
# A dictionary that contains all nodes you want to export with their names
|
||||
# NOTE: names should be globally unique
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ApplyMeshTransformAsShapeKey": ApplyMeshTransformAsShapeKey
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ApplyMeshTransformAsShapeKey": "Transform Shape Key"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
class CleanFace():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
# bpy.ops.mesh.delete(type='EDGE_FACE')
|
||||
bpy.ops.mesh.remove_doubles()
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
# bpy.ops.mesh.select_all(action='SELECT')
|
||||
# bpy.ops.mesh.edge_face_add()
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CleanFace": CleanFace
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"CleanFace": "Clean Face"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
class CombineMesh:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"obj_1": ("BPY_OBJS",),
|
||||
"obj_2": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, obj_1, obj_2):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
override = bpy.context.copy()
|
||||
override["active_object"] = obj_1[0]
|
||||
override["selected_editable_objects"] = list([*obj_1, *obj_2])
|
||||
bpy.ops.object.join(override)
|
||||
joined_object = override["active_object"]
|
||||
return ([joined_object], )
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CombineMesh": CombineMesh
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"CombineMesh": "Combine Mesh"
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
class CreateVertexBounds():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"name": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "Group"
|
||||
}),
|
||||
"extrude": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, name, extrude):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
if len(bpy_objs_target) == 0:
|
||||
# throw error
|
||||
return
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
bpy.ops.mesh.extrude_region_move(MESH_OT_extrude_region={
|
||||
"use_normal_flip": False, "use_dissolve_ortho_edges": False, "mirror": False})
|
||||
|
||||
bpy.ops.transform.resize(
|
||||
value=(extrude, extrude, 1), constraint_axis=(False, False, False))
|
||||
|
||||
# remove the extruded vertex from the vertex group "name"
|
||||
|
||||
bpy.ops.object.vertex_group_remove_from()
|
||||
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CreateVertexBounds": CreateVertexBounds
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"CreateVertexBounds": "Vertex Bounds"
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
class CreateVertexGroup():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"name": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "Group"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, name):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
if len(bpy_objs_target) == 0:
|
||||
# throw error
|
||||
return
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
# create vertex group and assign all selected faces to it (the name of the vertex group is the same as the name of the object)
|
||||
bpy.context.object.vertex_groups.new(name=name)
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CreateVertexGroup": CreateVertexGroup
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"CreateVertexGroup": "Vertex Group"
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
class DissolveFaces():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
# dissolve the faces
|
||||
# bpy.ops.mesh.dissolve_faces()
|
||||
bpy.ops.mesh.delete(type='ONLY_FACE')
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"DissolveFaces": DissolveFaces
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DissolveFaces": "Dissolve Faces"
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import folder_paths
|
||||
import os
|
||||
|
||||
class ExportGLTF():
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = ""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objects": ("BPY_OBJS",),
|
||||
"filename": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "out"
|
||||
}),
|
||||
"model_type": (["GLB", "GLTF_EMBEDDED"],),
|
||||
"write_mode": (["Overwrite", "Increment"],),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "process"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objects, filename, model_type, write_mode):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
# print(bpy, bpy_objects)
|
||||
|
||||
# deselect all objects
|
||||
override = bpy.context.copy()
|
||||
# print(override)
|
||||
# override["selected_objects"] = list(bpy.context.scene.objects)
|
||||
print(bpy_objects)
|
||||
override["selected_objects"] = list(bpy_objects)
|
||||
# override["active_object"] = list(context.scene.objects)[0]
|
||||
override["active_object"] = list(bpy_objects)[0]
|
||||
# override["active_object"] = None
|
||||
# override["window"] = {}
|
||||
# set count to the file count
|
||||
|
||||
filepath = self.output_dir + "/" + filename + '.' + ("glb" if model_type == "GLB" else "gltf")
|
||||
|
||||
if write_mode == "Increment":
|
||||
count = 0
|
||||
# while file exists, increment count
|
||||
while os.path.exists(self.output_dir + "/" + filename + '_' + str(count) + '.' + ("glb" if model_type == "GLB" else "gltf")):
|
||||
count += 1
|
||||
|
||||
filepath = self.output_dir + "/" + filename + '_' + str(count) + '.' + ("glb" if model_type == "GLB" else "gltf")
|
||||
|
||||
with bpy.context.temp_override(**override):
|
||||
bpy.ops.export_scene.gltf(filepath=filepath, export_format=model_type)
|
||||
print(filepath)
|
||||
|
||||
return { "ui" : { "gltfFilename": { filepath.replace(f"{self.output_dir}/", "") } } }
|
||||
|
||||
# A dictionary that contains all nodes you want to export with their names
|
||||
# NOTE: names should be globally unique
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ExportGLTF": ExportGLTF
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ExportGLTF": "ExportGLTF"
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
class FillFace:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
# fill the faces
|
||||
bpy.ops.mesh.fill()
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"FillFace": FillFace
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"FillFace": "Fill Face"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
class GroupObject():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"bpy_objs_target_2": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, bpy_objs_target_2):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
target_object2 = bpy_objs_target_2[0]
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"GroupObject": GroupObject
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"GroupObject": "Group Object"
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
class GroupVertexInside():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy": ("BPY",),
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"name": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "Group"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, name):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
|
||||
# select the vetex group's vertices
|
||||
bpy.ops.object.vertex_group_select()
|
||||
|
||||
# get vertex group
|
||||
vertex_group = target_object.vertex_groups[name]
|
||||
|
||||
verts = target_object.data.vertices
|
||||
|
||||
# all_vertices = [(v.co.x, v.co.y, i) for i, v in target_object.data.vertices]
|
||||
|
||||
# get the verts in the group
|
||||
verts_in_group = [v for v in verts if vertex_group.index in [vg.group for vg in v.groups]]
|
||||
|
||||
# get all selected vertices's xy
|
||||
# get the vertices of the vertex group
|
||||
vertex_group_vertices = [(v.co.x, v.co.y) for v in verts_in_group]
|
||||
|
||||
# get all vertices
|
||||
# all_vertices = [(v.co.x, v.co.y) for v in target_object.data.vertices]
|
||||
all_vertices_without_vertex_group = [(v.co.x, v.co.y, i) for i, v in enumerate(target_object.data.vertices) if v not in verts_in_group]
|
||||
|
||||
from scipy.spatial import ConvexHull, Delaunay
|
||||
|
||||
# create a convex hull of the vertex group's vertices
|
||||
hull = ConvexHull(vertex_group_vertices)
|
||||
|
||||
# create a Delaunay triangulation of the convex hull
|
||||
tri = Delaunay(hull.points[hull.vertices])
|
||||
|
||||
# get the indices of the vertices that are inside the convex hull
|
||||
inside_vertices_indices = [i for (x,y,i) in all_vertices_without_vertex_group if tri.find_simplex((x,y))>=0]
|
||||
|
||||
print(inside_vertices_indices)
|
||||
print(len(vertex_group_vertices))
|
||||
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
|
||||
# select the vertices that are inside the convex hull
|
||||
# for i in inside_vertices_indices:
|
||||
# target_object.data.vertices[i].select = True
|
||||
|
||||
# # select vertex from 1 to 10
|
||||
# for i in range(1, 100):
|
||||
# target_object.data.vertices[i].select = True
|
||||
|
||||
# create a new vertex group
|
||||
# bpy.context.object.vertex_groups.new(name="fuck")
|
||||
# bpy.ops.object.vertex_group_assign()
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
# create a new vertex group
|
||||
# new_group = bpy.context.object.vertex_groups.new(name="new_group")
|
||||
vertex_group.add(inside_vertices_indices, weight=1.0, type='REPLACE')
|
||||
|
||||
# assign the selected vertices to the new vertex group
|
||||
# new_group.add([v.index for v in target_object.data.vertices if v.select], weight=1.0, type='REPLACE')
|
||||
# new_group.add([20, 100], weight=1.0, type='REPLACE')
|
||||
# assign the selected vertices to the active vertex group
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"GroupVertexInside": GroupVertexInside
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"GroupVertexInside": "Group Vertex Inside"
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
class KnifeProjection:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_knife": ("BPY_OBJS",),
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_knife, bpy_objs_target):
|
||||
import mathutils
|
||||
import math
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
knife_object = bpy_objs_knife[0]
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# knife_object.select_set(True)
|
||||
# target_object.select_set(True)
|
||||
|
||||
# bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
area = next(
|
||||
area for area in bpy.context.screen.areas if area.type == 'VIEW_3D')
|
||||
space = next(space for space in area.spaces if space.type == 'VIEW_3D')
|
||||
region = next(
|
||||
region for region in area.regions if region.type == 'WINDOW')
|
||||
|
||||
# space.region_3d.view_perspective = 'ORTHO'
|
||||
# space.region_3d.view_rotation = mathutils.Euler(
|
||||
# (0, 0, 0)).to_quaternion()
|
||||
|
||||
# set blender to orthographic view without using numpad
|
||||
|
||||
# bpy.ops.view3d.view_axis(type='TOP')
|
||||
|
||||
override = bpy.context.copy()
|
||||
|
||||
# override["active_object"] = target_object
|
||||
# override["edit_object"] = target_object
|
||||
# override["selected_objects"] = [knife_object, target_object]
|
||||
# override["selected_editable_objects"] = [knife_object, target_object]
|
||||
|
||||
override["region"] = region
|
||||
override["area"] = area
|
||||
override["space"] = space
|
||||
|
||||
override["active_object"] = target_object
|
||||
override["edit_object"] = target_object
|
||||
override["selected_objects"] = [knife_object, target_object]
|
||||
override["selected_editable_objects"] = [knife_object, target_object]
|
||||
|
||||
knife_object.select_set(False)
|
||||
target_object.select_set(True)
|
||||
# move the kinfe object up a bit
|
||||
bpy.ops.transform.translate(value=(0, 0, -0.1))
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
knife_object.select_set(False)
|
||||
|
||||
# Get the viewport rotation
|
||||
viewport_rotation = space.region_3d.view_rotation
|
||||
|
||||
with bpy.context.temp_override(**override):
|
||||
# # Create a rotation matrix representing a top-down view
|
||||
# rotation_matrix = mathutils.Matrix.Rotation(math.radians(90.0), 4, 'X')
|
||||
|
||||
# # Create a rotation matrix to make the object face the user
|
||||
# rotation_matrix_face_user = mathutils.Matrix.Rotation(math.radians(180.0), 4, 'Z')
|
||||
|
||||
# # Create a rotation matrix for the viewport rotation
|
||||
# rotation_matrix_viewport = viewport_rotation.to_matrix().to_4x4()
|
||||
|
||||
# # Remember the original locations
|
||||
# original_location_knife = knife_object.location.copy()
|
||||
# original_location_target = target_object.location.copy()
|
||||
|
||||
# # Move the objects to the origin
|
||||
# knife_object.location = mathutils.Vector((0, 0, 0))
|
||||
# target_object.location = mathutils.Vector((0, 0, 0))
|
||||
|
||||
# # Rotate the objects
|
||||
# knife_object.matrix_world = rotation_matrix @ rotation_matrix_face_user @ rotation_matrix_viewport @ knife_object.matrix_world
|
||||
# target_object.matrix_world = rotation_matrix @ rotation_matrix_face_user @ rotation_matrix_viewport @ target_object.matrix_world
|
||||
|
||||
bpy.ops.view3d.view_axis(type='TOP', align_active=False)
|
||||
bpy.ops.view3d.view_persportho()
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
bpy.ops.mesh.knife_project()
|
||||
|
||||
# # Rotate the objects back
|
||||
# knife_object.matrix_world = rotation_matrix.inverted() @ knife_object.matrix_world
|
||||
# target_object.matrix_world = rotation_matrix.inverted() @ target_object.matrix_world
|
||||
|
||||
# # Move the objects back to their original locations
|
||||
# knife_object.location = original_location_knife
|
||||
# target_object.location = original_location_target
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"KnifeProjection": KnifeProjection
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"KnifeProjection": "Knife Projection"
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
class LoadBPY:
|
||||
def __init__(self):
|
||||
import bpy
|
||||
|
||||
self.bpy = bpy
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), # For disabling cache
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY",)
|
||||
RETURN_NAMES = ("bpy",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
# OUTPUT_NODE = False
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, seed):
|
||||
self.bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
return (self.bpy,)
|
||||
|
||||
|
||||
# A dictionary that contains all nodes you want to export with their names
|
||||
# NOTE: names should be globally unique
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LoadBPY": LoadBPY
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LoadBPY": "Load BPY"
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
class MeshFromTexture:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
# "min_threshold": ("INT", {"default": 50, "min": 0, "max": 1024}),
|
||||
# "max_threshold": ("INT", {"default": 150, "min": 0, "max": 1024}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), # For disabling cache
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE","BPY_OBJS")
|
||||
RETURN_NAMES = ("image","bpy_objs")
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
#OUTPUT_NODE = False
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, image, seed):
|
||||
import torch
|
||||
import cv2
|
||||
import numpy as np
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
image = np.copy(image[0].numpy())
|
||||
|
||||
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
||||
gray = (gray * 255).astype(np.uint8)
|
||||
# edges = cv2.Canny(gray, min_threshold, max_threshold)
|
||||
|
||||
# Find contours
|
||||
contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
def normalize_vertices(vertices, max_value):
|
||||
return vertices / float(max_value) * 2 - 1
|
||||
|
||||
# Get the image width and height
|
||||
height, width = image.shape[:2]
|
||||
|
||||
# Normalize the vertices
|
||||
normalized_contours = []
|
||||
for contour in contours:
|
||||
normalized_contour = []
|
||||
for vertex in contour:
|
||||
normalized_vertex = [normalize_vertices(vertex[0][0], width), normalize_vertices(vertex[0][1], height) * -1]
|
||||
normalized_contour.append(normalized_vertex)
|
||||
normalized_contours.append(np.array(normalized_contour, dtype=np.float32))
|
||||
|
||||
meshes = []
|
||||
# print(len(normalized_contours))
|
||||
for i, contour in enumerate(normalized_contours):
|
||||
mesh = bpy.data.meshes.new(name=f"NewMesh{i}") # Create a new mesh for each contour
|
||||
obj = bpy.data.objects.new(f"NewObject{i}", mesh) # Create a new object for each mesh
|
||||
bpy.context.collection.objects.link(obj) # Link the object to the current collection
|
||||
|
||||
ordered_vertices = [(*vertex, 0) for vertex in contour] # Add a z coordinate to each vertex
|
||||
face = list(range(len(ordered_vertices))) # Create a face from the vertices
|
||||
|
||||
mesh.from_pydata(ordered_vertices, [], [face]) # Create the mesh from the vertices and face
|
||||
|
||||
|
||||
|
||||
# Create a default shape key for the mesh
|
||||
sk_basis = obj.shape_key_add(name='Basis')
|
||||
|
||||
meshes.append(obj) # Add the object to the list of meshes
|
||||
|
||||
# Convert edges back to an image
|
||||
# image_from_edges = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
|
||||
# image_from_edges = image_from_edges.astype(np.float32)
|
||||
# image_from_edges = [torch.from_numpy(image_from_edges)]
|
||||
|
||||
# Draw contours on the original image
|
||||
if not image.flags['C_CONTIGUOUS']:
|
||||
image = np.ascontiguousarray(image)
|
||||
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
|
||||
|
||||
# print(image, meshes)
|
||||
|
||||
# Convert image back to tensor
|
||||
image = [torch.from_numpy(image)]
|
||||
return (image,meshes)
|
||||
|
||||
|
||||
# A dictionary that contains all nodes you want to export with their names
|
||||
# NOTE: names should be globally unique
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"MeshFromTexture": MeshFromTexture
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"MeshFromTexture": "Mesh from texture"
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import subprocess
|
||||
import folder_paths
|
||||
import os
|
||||
|
||||
blender_process_global = []
|
||||
|
||||
class OpenInBlender:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs": ("BPY_OBJS",),
|
||||
"blender_path": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "blender"
|
||||
}),
|
||||
"shading": (["Material", "Solid", "Rendered", "Wireframe"],),
|
||||
"camera_location": ("VECTOR3D",),
|
||||
"camera_rotation": ("VECTOR3D",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "process"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs, blender_path, shading, camera_location, camera_rotation):
|
||||
import mathutils
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
# Change shading mode and viewport
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == 'VIEW_3D':
|
||||
for space in area.spaces:
|
||||
if space.type == 'VIEW_3D':
|
||||
space.shading.type = shading.upper()
|
||||
rv3d = space.region_3d
|
||||
rv3d.view_location = camera_location
|
||||
rv3d.view_rotation = mathutils.Euler(camera_rotation).to_quaternion()
|
||||
|
||||
# Open blender
|
||||
if hasattr(self, 'blender_process'):
|
||||
self.blender_process.kill()
|
||||
# remove from global list so it doesn't get garbage collected
|
||||
blender_process_global.remove(self.blender_process)
|
||||
|
||||
# Save as .blend
|
||||
output_file = self.output_dir + '/tmp.blend'
|
||||
if os.path.exists(output_file):
|
||||
os.remove(output_file)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=output_file)
|
||||
|
||||
|
||||
self.blender_process = subprocess.Popen([blender_path, output_file])
|
||||
# append to global list so it doesn't get garbage collected
|
||||
blender_process_global.append(self.blender_process)
|
||||
return { "ui" : { } }
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"OpenInBlender": OpenInBlender
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"OpenInBlender": "Open in Blender"
|
||||
}
|
||||
|
||||
# detects when the python process is killed, and kills the blender process
|
||||
import atexit
|
||||
@atexit.register
|
||||
def kill_blender_process():
|
||||
print('blender_process_global', blender_process_global)
|
||||
for process in blender_process_global:
|
||||
process.kill()
|
||||
@@ -0,0 +1,72 @@
|
||||
class Plane:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"division": ("INT", {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 256,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), # For disabling cache
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, division, seed):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
# Define coordinates for the plane to object mode
|
||||
if bpy.context.active_object is not None:
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
coords = [(-1, -1, 0), (1, -1, 0), (-1, 1, 0), (1, 1, 0)]
|
||||
|
||||
# Create a mesh object
|
||||
mesh = bpy.data.meshes.new(name="PlaneMesh")
|
||||
mesh.from_pydata(coords,[],[(0,1,3,2)])
|
||||
|
||||
# Create a new object with the mesh
|
||||
object = bpy.data.objects.new(name="PlaneObject", object_data=mesh)
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.collection.objects.link(object)
|
||||
bpy.context.view_layer.objects.active = object
|
||||
object.select_set(True)
|
||||
|
||||
# Invert the Y axis of the UV to fix the vertical inversion
|
||||
bpy.ops.transform.resize(value=(-1, -1, 1))
|
||||
|
||||
# Create a new UV map
|
||||
uv_map = mesh.uv_layers.new(name="newUV")
|
||||
mesh.uv_layers.active = uv_map
|
||||
|
||||
# Unwrap the mesh
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.uv.unwrap()
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# Subdivide the plane
|
||||
if division > 0:
|
||||
original_mode = bpy.context.object.mode
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.subdivide(number_cuts=division)
|
||||
bpy.ops.object.mode_set(mode=original_mode)
|
||||
|
||||
return ([object],)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Plane": Plane
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Plane": "Plane"
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
class ScaleVertex():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
"extrude": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target, extrude):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
if len(bpy_objs_target) == 0:
|
||||
# throw error
|
||||
return
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
bpy.ops.transform.resize(
|
||||
value=(extrude, extrude, 1), constraint_axis=(False, False, False))
|
||||
|
||||
# remove the extruded vertex from the vertex group "name"
|
||||
# bpy.ops.object.vertex_group_remove_from()
|
||||
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ScaleVertex": ScaleVertex
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ScaleVertex": "Scale Vertex"
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
class Texture:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs": ("BPY_OBJS",),
|
||||
"texture": ("IMAGE",)
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
# OUTPUT_NODE = False
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs, texture):
|
||||
import numpy as np
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
# Convert image to numpy
|
||||
texture = texture[0].numpy()
|
||||
|
||||
# Flip the image vertically
|
||||
texture = np.flipud(texture)
|
||||
|
||||
# Create an image with the required dimensions
|
||||
img = bpy.data.images.new('my_image', width=texture.shape[1], height=texture.shape[0])
|
||||
|
||||
# If there is no alpha channel, append one full of 1's
|
||||
if texture.shape[2] == 3:
|
||||
alpha_channel = np.ones((*texture.shape[:2], 1))
|
||||
texture = np.concatenate((texture, alpha_channel), axis=2)
|
||||
|
||||
# Flatten image data and rearrange color channels for blender
|
||||
img.pixels = texture.ravel()
|
||||
|
||||
# Pack image to store it within .blend file
|
||||
img.pack()
|
||||
|
||||
# Save image to a file
|
||||
# img.filepath_raw = 'test.png'
|
||||
# img.file_format = 'PNG'
|
||||
# img.save()
|
||||
|
||||
# Get the active object
|
||||
obj = bpy_objs[0]
|
||||
|
||||
# Create a material
|
||||
mat = bpy.data.materials.new("MaterialName")
|
||||
mat.use_nodes = True
|
||||
nodes = mat.node_tree.nodes
|
||||
for node in nodes:
|
||||
nodes.remove(node)
|
||||
|
||||
# Add a new texture node
|
||||
texture_node = nodes.new(type='ShaderNodeTexImage')
|
||||
texture_node.image = img
|
||||
|
||||
# Add a new BSDF node
|
||||
bsdf_node = nodes.new(type='ShaderNodeBsdfPrincipled')
|
||||
|
||||
# Add a new output node
|
||||
output_node = nodes.new(type='ShaderNodeOutputMaterial')
|
||||
|
||||
# Link nodes together
|
||||
links = mat.node_tree.links
|
||||
links.new(bsdf_node.inputs['Base Color'], texture_node.outputs['Color'])
|
||||
links.new(output_node.inputs['Surface'], bsdf_node.outputs['BSDF'])
|
||||
|
||||
# Assign the material to the active object
|
||||
if obj.data.materials:
|
||||
obj.data.materials[0] = mat
|
||||
else:
|
||||
obj.data.materials.append(mat)
|
||||
return (bpy_objs,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Texture": Texture
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Texture": "Texture"
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
class UV_Unwrap():
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"bpy_objs_target": ("BPY_OBJS",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BPY_OBJS",)
|
||||
RETURN_NAMES = ("bpy_objs",)
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "mesh"
|
||||
|
||||
def process(self, bpy_objs_target):
|
||||
import global_bpy
|
||||
bpy = global_bpy.get_bpy()
|
||||
|
||||
target_object = bpy_objs_target[0]
|
||||
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
# deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
# select only the target object
|
||||
bpy.context.view_layer.objects.active = target_object
|
||||
# enter enter edit mode and select all faces of the object to fill
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
# perform a cube projection unwrap
|
||||
bpy.ops.uv.cube_project(cube_size=1.0, correct_aspect=True, clip_to_bounds=False, scale_to_bounds=True)
|
||||
|
||||
# bpy.ops.mesh.delete(type='FACE')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
return ([target_object],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UV_Unwrap": UV_Unwrap
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UV_Unwrap": "UV Unwrap"
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import torch
|
||||
from einops import rearrange
|
||||
from nodes import PreviewImage
|
||||
|
||||
class PointVisualizer(PreviewImage):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"point": ("VECTOR3D",),
|
||||
"point_size": ("INT", {
|
||||
"default": 1,
|
||||
"min": 10,
|
||||
"max": 20,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def process(self, images, point, point_size):
|
||||
x, y, z = point
|
||||
h, w, c = images[0].shape
|
||||
point_image = torch.zeros(images[0].shape)
|
||||
|
||||
center_x, center_y, _ = point
|
||||
top_left_x = center_x - point_size // 2
|
||||
top_left_y = center_y - point_size // 2
|
||||
|
||||
# Make sure the square is within the image boundaries
|
||||
top_left_x = max(0, min(w - point_size, top_left_x))
|
||||
top_left_y = max(0, min(h - point_size, top_left_y))
|
||||
|
||||
point_image[top_left_y:top_left_y+point_size, top_left_x:top_left_x+point_size] = 1
|
||||
point_image = rearrange(point_image, 'h w c -> 1 h w c')
|
||||
|
||||
# Blending
|
||||
images = images * 0.2 + point_image * 0.8
|
||||
return self.save_images(images)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"PointVisualizer": PointVisualizer
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"PointVisualizer": "Point Visualizer"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
class VECTOR3D:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"x": ("INT", {
|
||||
"default": 0,
|
||||
"min": -1024,
|
||||
"max": 1024,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"y": ("INT", {
|
||||
"default": 0,
|
||||
"min": -1024,
|
||||
"max": 1024,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"z": ("INT", {
|
||||
"default": 0,
|
||||
"min": -1024,
|
||||
"max": 1024,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("VECTOR3D",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "utils"
|
||||
|
||||
def run(self, x, y, z):
|
||||
return ([x, y, z],)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"VECTOR3D": VECTOR3D
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"VECTOR3D": "Vector 3D"
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import bpy
|
||||
global_bpy = bpy
|
||||
|
||||
def get_bpy():
|
||||
global global_bpy
|
||||
return global_bpy
|
||||
|
||||
def reset_bpy():
|
||||
global global_bpy
|
||||
bpy = global_bpy
|
||||
# not using this cause this cause seg fault frequently
|
||||
# global_bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
# Delete all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete()
|
||||
|
||||
# Delete all meshes
|
||||
for mesh in bpy.data.meshes:
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
# Delete all materials
|
||||
for material in bpy.data.materials:
|
||||
bpy.data.materials.remove(material)
|
||||
|
||||
# Delete all textures
|
||||
for texture in bpy.data.textures:
|
||||
bpy.data.textures.remove(texture)
|
||||
@@ -0,0 +1,112 @@
|
||||
import { app } from '../../scripts/app.js';
|
||||
import { api } from '../../scripts/api.js';
|
||||
import van from 'https://cdn.jsdelivr.net/gh/vanjs-org/van/public/van-1.1.3.min.js';
|
||||
|
||||
function addMenuHandler(nodeType, cb) {
|
||||
const getOpts = nodeType.prototype.getExtraMenuOptions;
|
||||
nodeType.prototype.getExtraMenuOptions = function () {
|
||||
const r = getOpts.apply(this, arguments);
|
||||
cb.apply(this, arguments);
|
||||
return r;
|
||||
};
|
||||
}
|
||||
|
||||
const iframeSrc = van.state('https://editor.avatech.ai');
|
||||
const showEditor = van.state(false);
|
||||
|
||||
function openInAvatechEditor(url, fileName) {
|
||||
let editor = document.getElementById('avatech-editor-iframe');
|
||||
iframeSrc.val = url;
|
||||
showEditor.val = true;
|
||||
|
||||
editor.contentWindow.postMessage(
|
||||
{
|
||||
key: 'key',
|
||||
value: fileName,
|
||||
method: 'store',
|
||||
},
|
||||
'*',
|
||||
);
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Avatech.Avatar.BlendshapeEditor',
|
||||
init(app) {},
|
||||
|
||||
async setup() {
|
||||
const { button, iframe, div } = van.tags;
|
||||
|
||||
const AvatarEditor = () => {
|
||||
return div(
|
||||
{
|
||||
class: 'fixed left-0 top-0 w-full h-full z-[200] pointer-events-none',
|
||||
id: 'avatech-editor',
|
||||
},
|
||||
button(
|
||||
{
|
||||
class: () =>
|
||||
'absolute left-0 top-0 z-[100] pointer-events-auto ' + (showEditor.val ? '' : 'hidden'),
|
||||
onclick: () => {
|
||||
console.log('close');
|
||||
showEditor.val = false;
|
||||
},
|
||||
},
|
||||
'back',
|
||||
),
|
||||
iframe({
|
||||
id: 'avatech-editor-iframe',
|
||||
title: 'avatech-editor-iframe',
|
||||
name: 'avatech-editor-iframe',
|
||||
class: () =>
|
||||
'w-full h-full pointer-events-auto ' +
|
||||
(showEditor.val ? '' : 'hidden'),
|
||||
src: iframeSrc,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
van.add(document.body, AvatarEditor());
|
||||
},
|
||||
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === 'ExportGLTF') {
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
const output = app.nodeOutputs[this.id + ""];
|
||||
if (!output.gltfFilename) return;
|
||||
|
||||
const gltfFilename = window.location.protocol + '//' + api.api_host + api.api_base + (`/view?filename=${output.gltfFilename[0]}`)
|
||||
|
||||
options.unshift({
|
||||
content: 'Save file',
|
||||
callback: () => {
|
||||
const a = document.createElement('a');
|
||||
let url = new URL(this.gltfFilename);
|
||||
url.searchParams.delete('preview');
|
||||
a.href = url;
|
||||
a.setAttribute(
|
||||
'download',
|
||||
new URLSearchParams(url.search).get('filename'),
|
||||
);
|
||||
document.body.append(a);
|
||||
a.click();
|
||||
requestAnimationFrame(() => a.remove());
|
||||
},
|
||||
});
|
||||
|
||||
options.unshift({
|
||||
content: 'Open In Avatech Editor (Local)',
|
||||
callback: () => {
|
||||
openInAvatechEditor('http://localhost:3006', gltfFilename);
|
||||
},
|
||||
});
|
||||
|
||||
options.unshift({
|
||||
content: 'Open In Avatech Editor',
|
||||
callback: () => {
|
||||
openInAvatechEditor('https://editor.avatech.ai', gltfFilename);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
let style = document.createElement('style');
|
||||
style.innerHTML = `.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.left-0{left:0}.top-0{top:0}.z-\\[100\\]{z-index:100}.z-\\[200\\]{z-index:200}.inline{display:inline}.hidden{display:none}.h-full{height:100%}.w-full{width:100%}`;
|
||||
document.head.appendChild(style);
|
||||
@@ -0,0 +1,3 @@
|
||||
/* @tailwind base; */
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "avatar-graph-comfyui",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"tailwindcss -i ./js/style.css -o ./js/output.css --watch -m\" \"node ./style-watcher.js\""
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"chokidar": "^3.5.3",
|
||||
"concurrently": "^8.2.1",
|
||||
"tailwindcss": "^3.3.3"
|
||||
}
|
||||
}
|
||||
Generated
+756
@@ -0,0 +1,756 @@
|
||||
lockfileVersion: '6.1'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
devDependencies:
|
||||
chokidar:
|
||||
specifier: ^3.5.3
|
||||
version: 3.5.3
|
||||
concurrently:
|
||||
specifier: ^8.2.1
|
||||
version: 8.2.1
|
||||
tailwindcss:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
|
||||
packages:
|
||||
|
||||
/@alloc/quick-lru@5.2.0:
|
||||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/@babel/runtime@7.22.11:
|
||||
resolution: {integrity: sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.0
|
||||
dev: true
|
||||
|
||||
/@jridgewell/gen-mapping@0.3.3:
|
||||
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dependencies:
|
||||
'@jridgewell/set-array': 1.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
'@jridgewell/trace-mapping': 0.3.19
|
||||
dev: true
|
||||
|
||||
/@jridgewell/resolve-uri@3.1.1:
|
||||
resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dev: true
|
||||
|
||||
/@jridgewell/set-array@1.1.2:
|
||||
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dev: true
|
||||
|
||||
/@jridgewell/sourcemap-codec@1.4.15:
|
||||
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
|
||||
dev: true
|
||||
|
||||
/@jridgewell/trace-mapping@0.3.19:
|
||||
resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==}
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.1
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
dev: true
|
||||
|
||||
/@nodelib/fs.scandir@2.1.5:
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
run-parallel: 1.2.0
|
||||
dev: true
|
||||
|
||||
/@nodelib/fs.stat@2.0.5:
|
||||
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
|
||||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/@nodelib/fs.walk@1.2.8:
|
||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.15.0
|
||||
dev: true
|
||||
|
||||
/ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
dev: true
|
||||
|
||||
/any-promise@1.3.0:
|
||||
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
||||
dev: true
|
||||
|
||||
/anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/arg@5.0.2:
|
||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||
dev: true
|
||||
|
||||
/balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
dev: true
|
||||
|
||||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/brace-expansion@1.1.11:
|
||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
concat-map: 0.0.1
|
||||
dev: true
|
||||
|
||||
/braces@3.0.2:
|
||||
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
fill-range: 7.0.1
|
||||
dev: true
|
||||
|
||||
/camelcase-css@2.0.1:
|
||||
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
dev: true
|
||||
|
||||
/chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
braces: 3.0.2
|
||||
glob-parent: 5.1.2
|
||||
is-binary-path: 2.1.0
|
||||
is-glob: 4.0.3
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
dev: true
|
||||
|
||||
/color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
dev: true
|
||||
|
||||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
dev: true
|
||||
|
||||
/commander@4.1.1:
|
||||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: true
|
||||
|
||||
/concurrently@8.2.1:
|
||||
resolution: {integrity: sha512-nVraf3aXOpIcNud5pB9M82p1tynmZkrSGQ1p6X/VY8cJ+2LMVqAgXsJxYYefACSHbTYlm92O1xuhdGTjwoEvbQ==}
|
||||
engines: {node: ^14.13.0 || >=16.0.0}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
date-fns: 2.30.0
|
||||
lodash: 4.17.21
|
||||
rxjs: 7.8.1
|
||||
shell-quote: 1.8.1
|
||||
spawn-command: 0.0.2
|
||||
supports-color: 8.1.1
|
||||
tree-kill: 1.2.2
|
||||
yargs: 17.7.2
|
||||
dev: true
|
||||
|
||||
/cssesc@3.0.0:
|
||||
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/date-fns@2.30.0:
|
||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
||||
engines: {node: '>=0.11'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.22.11
|
||||
dev: true
|
||||
|
||||
/didyoumean@1.2.2:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
dev: true
|
||||
|
||||
/dlv@1.1.3:
|
||||
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
dev: true
|
||||
|
||||
/escalade@3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/fast-glob@3.3.1:
|
||||
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
|
||||
engines: {node: '>=8.6.0'}
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
glob-parent: 5.1.2
|
||||
merge2: 1.4.1
|
||||
micromatch: 4.0.5
|
||||
dev: true
|
||||
|
||||
/fastq@1.15.0:
|
||||
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
|
||||
dependencies:
|
||||
reusify: 1.0.4
|
||||
dev: true
|
||||
|
||||
/fill-range@7.0.1:
|
||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
dev: true
|
||||
|
||||
/fs.realpath@1.0.0:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
dev: true
|
||||
|
||||
/fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/function-bind@1.1.1:
|
||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||
dev: true
|
||||
|
||||
/get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
dev: true
|
||||
|
||||
/glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
dev: true
|
||||
|
||||
/glob-parent@6.0.2:
|
||||
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
dev: true
|
||||
|
||||
/glob@7.1.6:
|
||||
resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 3.1.2
|
||||
once: 1.4.0
|
||||
path-is-absolute: 1.0.1
|
||||
dev: true
|
||||
|
||||
/has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/has@1.0.3:
|
||||
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
dependencies:
|
||||
function-bind: 1.1.1
|
||||
dev: true
|
||||
|
||||
/inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
wrappy: 1.0.2
|
||||
dev: true
|
||||
|
||||
/inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
dev: true
|
||||
|
||||
/is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
binary-extensions: 2.2.0
|
||||
dev: true
|
||||
|
||||
/is-core-module@2.13.0:
|
||||
resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==}
|
||||
dependencies:
|
||||
has: 1.0.3
|
||||
dev: true
|
||||
|
||||
/is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
dev: true
|
||||
|
||||
/is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
dev: true
|
||||
|
||||
/jiti@1.19.3:
|
||||
resolution: {integrity: sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w==}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/lilconfig@2.1.0:
|
||||
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
dev: true
|
||||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: true
|
||||
|
||||
/merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/micromatch@4.0.5:
|
||||
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
|
||||
engines: {node: '>=8.6'}
|
||||
dependencies:
|
||||
braces: 3.0.2
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
dependencies:
|
||||
brace-expansion: 1.1.11
|
||||
dev: true
|
||||
|
||||
/mz@2.7.0:
|
||||
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
|
||||
dependencies:
|
||||
any-promise: 1.3.0
|
||||
object-assign: 4.1.1
|
||||
thenify-all: 1.6.0
|
||||
dev: true
|
||||
|
||||
/nanoid@3.3.6:
|
||||
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/object-hash@3.0.0:
|
||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
dev: true
|
||||
|
||||
/path-is-absolute@1.0.1:
|
||||
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/path-parse@1.0.7:
|
||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||
dev: true
|
||||
|
||||
/picocolors@1.0.0:
|
||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||
dev: true
|
||||
|
||||
/picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
dev: true
|
||||
|
||||
/pify@2.3.0:
|
||||
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/pirates@4.0.6:
|
||||
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/postcss-import@15.1.0(postcss@8.4.29):
|
||||
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
postcss: ^8.0.0
|
||||
dependencies:
|
||||
postcss: 8.4.29
|
||||
postcss-value-parser: 4.2.0
|
||||
read-cache: 1.0.0
|
||||
resolve: 1.22.4
|
||||
dev: true
|
||||
|
||||
/postcss-js@4.0.1(postcss@8.4.29):
|
||||
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
|
||||
engines: {node: ^12 || ^14 || >= 16}
|
||||
peerDependencies:
|
||||
postcss: ^8.4.21
|
||||
dependencies:
|
||||
camelcase-css: 2.0.1
|
||||
postcss: 8.4.29
|
||||
dev: true
|
||||
|
||||
/postcss-load-config@4.0.1(postcss@8.4.29):
|
||||
resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==}
|
||||
engines: {node: '>= 14'}
|
||||
peerDependencies:
|
||||
postcss: '>=8.0.9'
|
||||
ts-node: '>=9.0.0'
|
||||
peerDependenciesMeta:
|
||||
postcss:
|
||||
optional: true
|
||||
ts-node:
|
||||
optional: true
|
||||
dependencies:
|
||||
lilconfig: 2.1.0
|
||||
postcss: 8.4.29
|
||||
yaml: 2.3.2
|
||||
dev: true
|
||||
|
||||
/postcss-nested@6.0.1(postcss@8.4.29):
|
||||
resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
|
||||
engines: {node: '>=12.0'}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.14
|
||||
dependencies:
|
||||
postcss: 8.4.29
|
||||
postcss-selector-parser: 6.0.13
|
||||
dev: true
|
||||
|
||||
/postcss-selector-parser@6.0.13:
|
||||
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
|
||||
engines: {node: '>=4'}
|
||||
dependencies:
|
||||
cssesc: 3.0.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/postcss-value-parser@4.2.0:
|
||||
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
||||
dev: true
|
||||
|
||||
/postcss@8.4.29:
|
||||
resolution: {integrity: sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
dependencies:
|
||||
nanoid: 3.3.6
|
||||
picocolors: 1.0.0
|
||||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
dev: true
|
||||
|
||||
/read-cache@1.0.0:
|
||||
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
||||
dependencies:
|
||||
pify: 2.3.0
|
||||
dev: true
|
||||
|
||||
/readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/regenerator-runtime@0.14.0:
|
||||
resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==}
|
||||
dev: true
|
||||
|
||||
/require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/resolve@1.22.4:
|
||||
resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
is-core-module: 2.13.0
|
||||
path-parse: 1.0.7
|
||||
supports-preserve-symlinks-flag: 1.0.0
|
||||
dev: true
|
||||
|
||||
/reusify@1.0.4:
|
||||
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
dev: true
|
||||
|
||||
/rxjs@7.8.1:
|
||||
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
dev: true
|
||||
|
||||
/shell-quote@1.8.1:
|
||||
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
|
||||
dev: true
|
||||
|
||||
/source-map-js@1.0.2:
|
||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/spawn-command@0.0.2:
|
||||
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
|
||||
dev: true
|
||||
|
||||
/string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
dev: true
|
||||
|
||||
/sucrase@3.34.0:
|
||||
resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.3
|
||||
commander: 4.1.1
|
||||
glob: 7.1.6
|
||||
lines-and-columns: 1.2.4
|
||||
mz: 2.7.0
|
||||
pirates: 4.0.6
|
||||
ts-interface-checker: 0.1.13
|
||||
dev: true
|
||||
|
||||
/supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
dev: true
|
||||
|
||||
/supports-color@8.1.1:
|
||||
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
dev: true
|
||||
|
||||
/supports-preserve-symlinks-flag@1.0.0:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/tailwindcss@3.3.3:
|
||||
resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@alloc/quick-lru': 5.2.0
|
||||
arg: 5.0.2
|
||||
chokidar: 3.5.3
|
||||
didyoumean: 1.2.2
|
||||
dlv: 1.1.3
|
||||
fast-glob: 3.3.1
|
||||
glob-parent: 6.0.2
|
||||
is-glob: 4.0.3
|
||||
jiti: 1.19.3
|
||||
lilconfig: 2.1.0
|
||||
micromatch: 4.0.5
|
||||
normalize-path: 3.0.0
|
||||
object-hash: 3.0.0
|
||||
picocolors: 1.0.0
|
||||
postcss: 8.4.29
|
||||
postcss-import: 15.1.0(postcss@8.4.29)
|
||||
postcss-js: 4.0.1(postcss@8.4.29)
|
||||
postcss-load-config: 4.0.1(postcss@8.4.29)
|
||||
postcss-nested: 6.0.1(postcss@8.4.29)
|
||||
postcss-selector-parser: 6.0.13
|
||||
resolve: 1.22.4
|
||||
sucrase: 3.34.0
|
||||
transitivePeerDependencies:
|
||||
- ts-node
|
||||
dev: true
|
||||
|
||||
/thenify-all@1.6.0:
|
||||
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
|
||||
engines: {node: '>=0.8'}
|
||||
dependencies:
|
||||
thenify: 3.3.1
|
||||
dev: true
|
||||
|
||||
/thenify@3.3.1:
|
||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||
dependencies:
|
||||
any-promise: 1.3.0
|
||||
dev: true
|
||||
|
||||
/to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
dev: true
|
||||
|
||||
/tree-kill@1.2.2:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/ts-interface-checker@0.1.13:
|
||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||
dev: true
|
||||
|
||||
/tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
dev: true
|
||||
|
||||
/util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
dev: true
|
||||
|
||||
/wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
dev: true
|
||||
|
||||
/y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/yaml@2.3.2:
|
||||
resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==}
|
||||
engines: {node: '>= 14'}
|
||||
dev: true
|
||||
|
||||
/yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/yargs@17.7.2:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
cliui: 8.0.1
|
||||
escalade: 3.1.1
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
string-width: 4.2.3
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
dev: true
|
||||
@@ -0,0 +1,8 @@
|
||||
torchvision
|
||||
numpy
|
||||
opencv-python
|
||||
opencv-contrib-python
|
||||
einops
|
||||
bpy
|
||||
segment-anything
|
||||
# -e git+https://github.com/facebookresearch/segment-anything.git#egg=segment_anything
|
||||
@@ -0,0 +1,42 @@
|
||||
import folder_paths
|
||||
import torch
|
||||
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry
|
||||
from einops import rearrange, repeat
|
||||
import requests
|
||||
from PIL import Image
|
||||
import io
|
||||
import numpy as np
|
||||
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry, SamPredictor
|
||||
from einops import rearrange, repeat
|
||||
|
||||
class SAM_Remote_Emb:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"model_type": (["vit_h", "vit_l", "vit_b"],),
|
||||
"ckpt": (folder_paths.get_filename_list("sam"),),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SAM", "SAMPREDICTOR")
|
||||
RETURN_NAMES = ("sam", "predictor")
|
||||
|
||||
FUNCTION = "segment"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def segment(self, model_type, ckpt):
|
||||
ckpt = folder_paths.get_full_path("sam", ckpt)
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt)
|
||||
predictor = SamPredictor(sam)
|
||||
|
||||
return (sam, predictor)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SAM_Loader": SAM_Remote_Emb
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SAM_Loader": "SAM Loader"
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import folder_paths
|
||||
import torch
|
||||
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry
|
||||
from einops import rearrange, repeat
|
||||
|
||||
class SAM:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"point_1": ("VECTOR3D",),
|
||||
"model_type": (["vit_h", "vit_l", "vit_b"],),
|
||||
"ckpt": (folder_paths.get_filename_list("sam"),),
|
||||
},
|
||||
"optional": {
|
||||
"point_2": ("VECTOR3D",),
|
||||
"point_3": ("VECTOR3D",),
|
||||
"point_4": ("VECTOR3D",),
|
||||
"point_5": ("VECTOR3D",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "IMAGE", "IMAGE", "IMAGE", "IMAGE", "IMAGE", "IMAGE", "IMAGE")
|
||||
RETURN_NAMES = ("mask_1", "image_1", "mask_2", "image_2", "mask_3", "image_3", "mask_4", "image_4", "mask_5", "image_5",)
|
||||
|
||||
FUNCTION = "segment"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def is_point_in_bbox(self, point, mask):
|
||||
x, y, z = point
|
||||
in_bbox = mask[y, x].item() if y < mask.shape[0] and x < mask.shape[1] else None
|
||||
return in_bbox
|
||||
|
||||
def find_largest_bbox(self, point, masks):
|
||||
largest_bbox = None
|
||||
largest_area = 0
|
||||
|
||||
for mask in masks:
|
||||
area = mask['area']
|
||||
if self.is_point_in_bbox(point, mask['segmentation']) and area > largest_area:
|
||||
largest_bbox = mask
|
||||
largest_area = area
|
||||
|
||||
return largest_bbox
|
||||
|
||||
def segment(self, image, point_1, model_type, ckpt, point_2=None, point_3=None, point_4=None, point_5=None):
|
||||
image = (image[0] * 255).to(torch.uint8).numpy()
|
||||
H, W, C = image.shape
|
||||
ckpt = folder_paths.get_full_path("sam", ckpt)
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt) #.to("mps")
|
||||
mask_generator = SamAutomaticMaskGenerator(sam)
|
||||
masks = mask_generator.generate(image)
|
||||
points = [point_1, point_2, point_3, point_4, point_5]
|
||||
|
||||
outputs = []
|
||||
for point in points:
|
||||
if point is not None:
|
||||
bbox = self.find_largest_bbox(point, masks)
|
||||
mask = torch.from_numpy(bbox["segmentation"]) if bbox is not None else torch.zeros(H, W)
|
||||
mask = repeat(mask, 'h w -> h w c', c=3)
|
||||
out_image = torch.from_numpy(image) * mask
|
||||
|
||||
mask = rearrange(mask, 'h w c -> 1 h w c')
|
||||
out_image = rearrange(out_image, 'h w c -> 1 h w c') / 255
|
||||
|
||||
outputs.append(mask)
|
||||
outputs.append(out_image)
|
||||
else:
|
||||
outputs.append(None)
|
||||
outputs.append(None)
|
||||
return outputs
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Segmentation": SAM
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Segmentation": "Segmentation Node"
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import torch
|
||||
from einops import rearrange, repeat
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
class SAM_Predict:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"embeddings": ("EMBEDDINGS",),
|
||||
"predictor": ("SAMPREDICTOR",)
|
||||
},
|
||||
"optional": {
|
||||
"point_1": ("VECTOR3D",),
|
||||
"mask": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE","IMAGE", "IMAGE")
|
||||
RETURN_NAMES = ("image","out_image", "mask")
|
||||
|
||||
FUNCTION = "segment"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def segment(self, image, embeddings, predictor, point_1=None, mask=None):
|
||||
image_embedding_list = embeddings['image_embedding']
|
||||
shape = tuple(embeddings['shape'])
|
||||
input_size = tuple(embeddings['input_size'])
|
||||
|
||||
# Convert the list back to a numpy array with the original shape
|
||||
image_embedding_np = np.array(image_embedding_list, dtype=np.single).reshape(shape)
|
||||
|
||||
# Convert the numpy array to a PyTorch tensor
|
||||
image_embedding_tensor = torch.from_numpy(image_embedding_np)
|
||||
|
||||
# Set the image embeddings for the model
|
||||
# predictor.set_torch_image(image_embedding_tensor, image.shape[:2])
|
||||
# print(image[0].shape[:2])
|
||||
predictor.input_size = input_size
|
||||
predictor.features = image_embedding_tensor
|
||||
predictor.is_image_set = True
|
||||
predictor.original_size = image[0].shape[:2]
|
||||
|
||||
if point_1 != None:
|
||||
x, y, z = point_1
|
||||
point_coords = np.array([[x, y]])
|
||||
point_labels = np.array([1])
|
||||
masks, iou_predictions, low_res_masks = predictor.predict(
|
||||
point_coords=point_coords,
|
||||
point_labels=point_labels,
|
||||
)
|
||||
elif mask != None:
|
||||
# scale the mask to 256x256
|
||||
cv2_mask = cv2.resize(np.array(mask[0]), (256, 256))
|
||||
cv2_mask = cv2_mask[np.newaxis, :, :]
|
||||
cv2_mask = (cv2_mask * 255).astype(int)
|
||||
|
||||
true_locations = np.array(np.where(cv2_mask[0] == 255))
|
||||
if true_locations.shape[1] > 0:
|
||||
# Randomly select a point in the mask
|
||||
rand_index = np.random.randint(true_locations.shape[1])
|
||||
y, x = true_locations[:, rand_index]
|
||||
point_coords = np.array([[x, y]])
|
||||
point_labels = np.array([1])
|
||||
|
||||
masks, iou_predictions, low_res_masks = predictor.predict(
|
||||
point_coords=point_coords,
|
||||
point_labels=point_labels,
|
||||
mask_input=cv2_mask
|
||||
)
|
||||
else:
|
||||
# No detected mask
|
||||
h, w, c = image[0].shape
|
||||
masks = np.zeros((1, h, w))
|
||||
|
||||
masks = torch.from_numpy(masks)
|
||||
|
||||
masks = rearrange(masks[0], 'h w -> 1 h w')
|
||||
# masks = rearrange(masks, 'c h w -> 1 c h w')
|
||||
out_image = repeat(masks, '1 h w -> 1 h w c', c=3) * image
|
||||
|
||||
print(masks.shape, torch.max(masks), torch.min(masks))
|
||||
print(image.shape, torch.max(image), torch.min(image))
|
||||
|
||||
# print(emb)
|
||||
|
||||
# print(masks, out_image)
|
||||
return (image, out_image, masks)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SAM_Predict": SAM_Predict
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SAM_Predict": "SAM Predict"
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import requests
|
||||
from PIL import Image
|
||||
import io
|
||||
import numpy as np
|
||||
|
||||
class SAM_Embedding:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
},
|
||||
"optional": {
|
||||
"predictor": ("SAMPREDICTOR",)
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("EMBEDDINGS", )
|
||||
RETURN_NAMES = ("embeddings", )
|
||||
|
||||
FUNCTION = "segment"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def segment(self, image, predictor=None):
|
||||
# Convert PyTorch tensor to numpy array
|
||||
image_np = (image[0].numpy() * 255).astype(np.uint8)
|
||||
|
||||
if predictor != None:
|
||||
predictor.set_image(image_np)
|
||||
emb = predictor.get_image_embedding().cpu().numpy()
|
||||
output = {
|
||||
"image_embedding": emb,
|
||||
"shape": emb.shape,
|
||||
"input_size": predictor.input_size
|
||||
}
|
||||
else:
|
||||
# Convert numpy array to PIL Image
|
||||
img = Image.fromarray(image_np)
|
||||
|
||||
# Create an in-memory bytes buffer
|
||||
img_byte_arr = io.BytesIO()
|
||||
|
||||
# Save the PIL Image to the bytes buffer in PNG format
|
||||
img.save(img_byte_arr, format='PNG')
|
||||
|
||||
# Get the bytes value of the buffer
|
||||
img_byte_arr = img_byte_arr.getvalue()
|
||||
|
||||
# Create a dictionary with the image bytes
|
||||
files = {'image': ('image.png', img_byte_arr)}
|
||||
|
||||
# Send the POST request
|
||||
response = requests.post('https://avatechgg--segment-anything-entrypoint.modal.run', files=files)
|
||||
|
||||
# Check if the request was successful
|
||||
if response.status_code == 200:
|
||||
# Parse the JSON response
|
||||
output = response.json()
|
||||
else:
|
||||
print(f"Request failed with status code {response.status_code}")
|
||||
output = None
|
||||
|
||||
# sam = sam_model_registry[model_type](checkpoint=ckpt)
|
||||
# predictor = SamPredictor(sam)
|
||||
# masks = predictor.set_torch_image
|
||||
# masks = predictor.predict
|
||||
|
||||
# print(output)
|
||||
|
||||
return (output, )
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SAM_Embedding": SAM_Embedding
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SAM_Embedding": "SAM Embedding"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
const fs = require('fs');
|
||||
const chokidar = require('chokidar');
|
||||
const path = require('path');
|
||||
|
||||
// Initialize watcher.
|
||||
const watcher = chokidar.watch(path.resolve(__dirname, './js/output.css'), {
|
||||
persistent: true
|
||||
});
|
||||
|
||||
// Add event listeners.
|
||||
watcher
|
||||
.on('change', filePath => {
|
||||
console.log(`File ${filePath} has been changed`);
|
||||
let cssContent = fs.readFileSync(filePath, 'utf8');
|
||||
let jsContent = `let style = document.createElement('style');
|
||||
style.innerHTML = \`${cssContent.replace(/\\/g, '\\\\')}\`;
|
||||
document.head.appendChild(style);`;
|
||||
fs.writeFileSync(path.resolve(__dirname, './js/style-inject.js'), jsContent);
|
||||
});
|
||||
|
||||
// On script exit, close the watcher
|
||||
process.on('exit', (code) => {
|
||||
watcher.close();
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
// content: ['./js/**/*.{html,js}'],
|
||||
content: ['./js/blendshape_editor.js'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user