4d236f269e1a886ee94de597a01280b7022bdb78bc9f29135e2270c87e807b5a
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +2 -0
- openpose-editor/scripts/openpose/util.py +166 -0
- posex/.gitignore +3 -0
- posex/README.md +98 -0
- posex/app.py +85 -0
- posex/common/__pycache__/posex_utils.cpython-310.pyc +0 -0
- posex/common/posex_utils.py +111 -0
- posex/css/main.css +125 -0
- posex/image/favicon.ico +0 -0
- posex/image/favicon.png +0 -0
- posex/image/sample-webui.png +0 -0
- posex/image/sample-webui2.jpg +3 -0
- posex/image/sample-webui2.png +3 -0
- posex/image/sample-webui3.png +0 -0
- posex/image/sample.png +0 -0
- posex/index.html +74 -0
- posex/javascript/lazyload/posex-webui.js +386 -0
- posex/javascript/posex-webui.js +93 -0
- posex/js/DragControls.js +228 -0
- posex/js/LICENSE/THREE.MeshLine +21 -0
- posex/js/LICENSE/es-module-shims +10 -0
- posex/js/LICENSE/three +21 -0
- posex/js/THREE.MeshLine.Module.min.js +15 -0
- posex/js/TrackballControls.js +821 -0
- posex/js/app.js +145 -0
- posex/js/es-module-shims.js +790 -0
- posex/js/posex.js +844 -0
- posex/js/three.module.js +0 -0
- posex/requirements.txt +2 -0
- posex/saved_poses/bridge.png +0 -0
- posex/saved_poses/sample.png +0 -0
- posex/scripts/__pycache__/posex.cpython-310.pyc +0 -0
- posex/scripts/posex.py +184 -0
- posex/style.css +159 -0
- put extensions here.txt +0 -0
- sd-webui-lora-block-weight/README.md +350 -0
- sd-webui-lora-block-weight/scripts/Roboto-Regular.ttf +0 -0
- sd-webui-lora-block-weight/scripts/__pycache__/lora_block_weight.cpython-310.pyc +0 -0
- sd-webui-lora-block-weight/scripts/elempresets.txt +7 -0
- sd-webui-lora-block-weight/scripts/lbwpresets.txt +10 -0
- sd-webui-lora-block-weight/scripts/lora_block_weight.py +744 -0
- sd-webui-stablesr/.gitignore +7 -0
- sd-webui-stablesr/LICENSE +35 -0
- sd-webui-stablesr/LICENSE2 +437 -0
- sd-webui-stablesr/README.md +156 -0
- sd-webui-stablesr/README_CN.md +150 -0
- sd-webui-stablesr/scripts/__pycache__/stablesr.cpython-310.pyc +0 -0
- sd-webui-stablesr/scripts/stablesr.py +276 -0
- sd-webui-stablesr/srmodule/__pycache__/attn.cpython-310.pyc +0 -0
- sd-webui-stablesr/srmodule/__pycache__/colorfix.cpython-310.pyc +0 -0
.gitattributes
CHANGED
@@ -37,3 +37,5 @@ Auto-Photoshop-StableDiffusion-Plugin/docs/inpainting.gif filter=lfs diff=lfs me
|
|
37 |
Auto-Photoshop-StableDiffusion-Plugin/docs/outpainting.gif filter=lfs diff=lfs merge=lfs -text
|
38 |
Auto-Photoshop-StableDiffusion-Plugin/docs/prompt_shortcut.gif filter=lfs diff=lfs merge=lfs -text
|
39 |
Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
37 |
Auto-Photoshop-StableDiffusion-Plugin/docs/outpainting.gif filter=lfs diff=lfs merge=lfs -text
|
38 |
Auto-Photoshop-StableDiffusion-Plugin/docs/prompt_shortcut.gif filter=lfs diff=lfs merge=lfs -text
|
39 |
Stable-Diffusion-Webui-Civitai-Helper/img/all_in_one.png filter=lfs diff=lfs merge=lfs -text
|
40 |
+
posex/image/sample-webui2.jpg filter=lfs diff=lfs merge=lfs -text
|
41 |
+
posex/image/sample-webui2.png filter=lfs diff=lfs merge=lfs -text
|
openpose-editor/scripts/openpose/util.py
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This code from https://github.com/lllyasviel/ControlNet
|
2 |
+
|
3 |
+
import math
|
4 |
+
import numpy as np
|
5 |
+
import matplotlib
|
6 |
+
import cv2
|
7 |
+
|
8 |
+
|
9 |
+
def padRightDownCorner(img, stride, padValue):
|
10 |
+
h = img.shape[0]
|
11 |
+
w = img.shape[1]
|
12 |
+
|
13 |
+
pad = 4 * [None]
|
14 |
+
pad[0] = 0 # up
|
15 |
+
pad[1] = 0 # left
|
16 |
+
pad[2] = 0 if (h % stride == 0) else stride - (h % stride) # down
|
17 |
+
pad[3] = 0 if (w % stride == 0) else stride - (w % stride) # right
|
18 |
+
|
19 |
+
img_padded = img
|
20 |
+
pad_up = np.tile(img_padded[0:1, :, :]*0 + padValue, (pad[0], 1, 1))
|
21 |
+
img_padded = np.concatenate((pad_up, img_padded), axis=0)
|
22 |
+
pad_left = np.tile(img_padded[:, 0:1, :]*0 + padValue, (1, pad[1], 1))
|
23 |
+
img_padded = np.concatenate((pad_left, img_padded), axis=1)
|
24 |
+
pad_down = np.tile(img_padded[-2:-1, :, :]*0 + padValue, (pad[2], 1, 1))
|
25 |
+
img_padded = np.concatenate((img_padded, pad_down), axis=0)
|
26 |
+
pad_right = np.tile(img_padded[:, -2:-1, :]*0 + padValue, (1, pad[3], 1))
|
27 |
+
img_padded = np.concatenate((img_padded, pad_right), axis=1)
|
28 |
+
|
29 |
+
return img_padded, pad
|
30 |
+
|
31 |
+
# transfer caffe model to pytorch which will match the layer name
|
32 |
+
def transfer(model, model_weights):
|
33 |
+
transferred_model_weights = {}
|
34 |
+
for weights_name in model.state_dict().keys():
|
35 |
+
transferred_model_weights[weights_name] = model_weights['.'.join(weights_name.split('.')[1:])]
|
36 |
+
return transferred_model_weights
|
37 |
+
|
38 |
+
# draw the body keypoint and lims
|
39 |
+
def draw_bodypose(canvas, candidate, subset):
|
40 |
+
stickwidth = 4
|
41 |
+
limbSeq = [[2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10], \
|
42 |
+
[10, 11], [2, 12], [12, 13], [13, 14], [2, 1], [1, 15], [15, 17], \
|
43 |
+
[1, 16], [16, 18], [3, 17], [6, 18]]
|
44 |
+
|
45 |
+
colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0], \
|
46 |
+
[0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], \
|
47 |
+
[170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]]
|
48 |
+
for i in range(18):
|
49 |
+
for n in range(len(subset)):
|
50 |
+
index = int(subset[n][i])
|
51 |
+
if index == -1:
|
52 |
+
continue
|
53 |
+
x, y = candidate[index][0:2]
|
54 |
+
cv2.circle(canvas, (int(x), int(y)), 4, colors[i], thickness=-1)
|
55 |
+
for i in range(17):
|
56 |
+
for n in range(len(subset)):
|
57 |
+
index = subset[n][np.array(limbSeq[i]) - 1]
|
58 |
+
if -1 in index:
|
59 |
+
continue
|
60 |
+
cur_canvas = canvas.copy()
|
61 |
+
Y = candidate[index.astype(int), 0]
|
62 |
+
X = candidate[index.astype(int), 1]
|
63 |
+
mX = np.mean(X)
|
64 |
+
mY = np.mean(Y)
|
65 |
+
length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
|
66 |
+
angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
|
67 |
+
polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
|
68 |
+
cv2.fillConvexPoly(cur_canvas, polygon, colors[i])
|
69 |
+
canvas = cv2.addWeighted(canvas, 0.4, cur_canvas, 0.6, 0)
|
70 |
+
# plt.imsave("preview.jpg", canvas[:, :, [2, 1, 0]])
|
71 |
+
# plt.imshow(canvas[:, :, [2, 1, 0]])
|
72 |
+
return canvas
|
73 |
+
|
74 |
+
|
75 |
+
# image drawn by opencv is not good.
|
76 |
+
def draw_handpose(canvas, all_hand_peaks, show_number=False):
|
77 |
+
edges = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [0, 9], [9, 10], \
|
78 |
+
[10, 11], [11, 12], [0, 13], [13, 14], [14, 15], [15, 16], [0, 17], [17, 18], [18, 19], [19, 20]]
|
79 |
+
|
80 |
+
for peaks in all_hand_peaks:
|
81 |
+
for ie, e in enumerate(edges):
|
82 |
+
if np.sum(np.all(peaks[e], axis=1)==0)==0:
|
83 |
+
x1, y1 = peaks[e[0]]
|
84 |
+
x2, y2 = peaks[e[1]]
|
85 |
+
cv2.line(canvas, (x1, y1), (x2, y2), matplotlib.colors.hsv_to_rgb([ie/float(len(edges)), 1.0, 1.0])*255, thickness=2)
|
86 |
+
|
87 |
+
for i, keyponit in enumerate(peaks):
|
88 |
+
x, y = keyponit
|
89 |
+
cv2.circle(canvas, (x, y), 4, (0, 0, 255), thickness=-1)
|
90 |
+
if show_number:
|
91 |
+
cv2.putText(canvas, str(i), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (0, 0, 0), lineType=cv2.LINE_AA)
|
92 |
+
return canvas
|
93 |
+
|
94 |
+
# detect hand according to body pose keypoints
|
95 |
+
# please refer to https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/src/openpose/hand/handDetector.cpp
|
96 |
+
def handDetect(candidate, subset, oriImg):
|
97 |
+
# right hand: wrist 4, elbow 3, shoulder 2
|
98 |
+
# left hand: wrist 7, elbow 6, shoulder 5
|
99 |
+
ratioWristElbow = 0.33
|
100 |
+
detect_result = []
|
101 |
+
image_height, image_width = oriImg.shape[0:2]
|
102 |
+
for person in subset.astype(int):
|
103 |
+
# if any of three not detected
|
104 |
+
has_left = np.sum(person[[5, 6, 7]] == -1) == 0
|
105 |
+
has_right = np.sum(person[[2, 3, 4]] == -1) == 0
|
106 |
+
if not (has_left or has_right):
|
107 |
+
continue
|
108 |
+
hands = []
|
109 |
+
#left hand
|
110 |
+
if has_left:
|
111 |
+
left_shoulder_index, left_elbow_index, left_wrist_index = person[[5, 6, 7]]
|
112 |
+
x1, y1 = candidate[left_shoulder_index][:2]
|
113 |
+
x2, y2 = candidate[left_elbow_index][:2]
|
114 |
+
x3, y3 = candidate[left_wrist_index][:2]
|
115 |
+
hands.append([x1, y1, x2, y2, x3, y3, True])
|
116 |
+
# right hand
|
117 |
+
if has_right:
|
118 |
+
right_shoulder_index, right_elbow_index, right_wrist_index = person[[2, 3, 4]]
|
119 |
+
x1, y1 = candidate[right_shoulder_index][:2]
|
120 |
+
x2, y2 = candidate[right_elbow_index][:2]
|
121 |
+
x3, y3 = candidate[right_wrist_index][:2]
|
122 |
+
hands.append([x1, y1, x2, y2, x3, y3, False])
|
123 |
+
|
124 |
+
for x1, y1, x2, y2, x3, y3, is_left in hands:
|
125 |
+
# pos_hand = pos_wrist + ratio * (pos_wrist - pos_elbox) = (1 + ratio) * pos_wrist - ratio * pos_elbox
|
126 |
+
# handRectangle.x = posePtr[wrist*3] + ratioWristElbow * (posePtr[wrist*3] - posePtr[elbow*3]);
|
127 |
+
# handRectangle.y = posePtr[wrist*3+1] + ratioWristElbow * (posePtr[wrist*3+1] - posePtr[elbow*3+1]);
|
128 |
+
# const auto distanceWristElbow = getDistance(poseKeypoints, person, wrist, elbow);
|
129 |
+
# const auto distanceElbowShoulder = getDistance(poseKeypoints, person, elbow, shoulder);
|
130 |
+
# handRectangle.width = 1.5f * fastMax(distanceWristElbow, 0.9f * distanceElbowShoulder);
|
131 |
+
x = x3 + ratioWristElbow * (x3 - x2)
|
132 |
+
y = y3 + ratioWristElbow * (y3 - y2)
|
133 |
+
distanceWristElbow = math.sqrt((x3 - x2) ** 2 + (y3 - y2) ** 2)
|
134 |
+
distanceElbowShoulder = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
|
135 |
+
width = 1.5 * max(distanceWristElbow, 0.9 * distanceElbowShoulder)
|
136 |
+
# x-y refers to the center --> offset to topLeft point
|
137 |
+
# handRectangle.x -= handRectangle.width / 2.f;
|
138 |
+
# handRectangle.y -= handRectangle.height / 2.f;
|
139 |
+
x -= width / 2
|
140 |
+
y -= width / 2 # width = height
|
141 |
+
# overflow the image
|
142 |
+
if x < 0: x = 0
|
143 |
+
if y < 0: y = 0
|
144 |
+
width1 = width
|
145 |
+
width2 = width
|
146 |
+
if x + width > image_width: width1 = image_width - x
|
147 |
+
if y + width > image_height: width2 = image_height - y
|
148 |
+
width = min(width1, width2)
|
149 |
+
# the max hand box value is 20 pixels
|
150 |
+
if width >= 20:
|
151 |
+
detect_result.append([int(x), int(y), int(width), is_left])
|
152 |
+
|
153 |
+
'''
|
154 |
+
return value: [[x, y, w, True if left hand else False]].
|
155 |
+
width=height since the network require squared input.
|
156 |
+
x, y is the coordinate of top left
|
157 |
+
'''
|
158 |
+
return detect_result
|
159 |
+
|
160 |
+
# get max index of 2d array
|
161 |
+
def npmax(array):
|
162 |
+
arrayindex = array.argmax(1)
|
163 |
+
arrayvalue = array.max(1)
|
164 |
+
i = arrayvalue.argmax()
|
165 |
+
j = arrayindex[i]
|
166 |
+
return i, j
|
posex/.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
__pycache__
|
2 |
+
.vscode/
|
3 |
+
venv/
|
posex/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Posex - Estimated Image Generator for Pose2Image
|
2 |
+
|
3 |
+
![cover](./image/sample.png)
|
4 |
+
|
5 |
+
## Quick Start with [Web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui)
|
6 |
+
|
7 |
+
1. Install [Mikubill/sd-webui-controlnet](https://github.com/Mikubill/sd-webui-controlnet).
|
8 |
+
2. Install Posex (this).
|
9 |
+
3. Open `Posex` accordion in t2i tab (or i2i as you like). Enable `Send this image to ControlNet` checkbox. Editor will appear.
|
10 |
+
4. Configure ControlNet as below.
|
11 |
+
```
|
12 |
+
Preprocessor: none
|
13 |
+
Model: control_sd15_openpose
|
14 |
+
```
|
15 |
+
5. Make pose.
|
16 |
+
6. Generate images.
|
17 |
+
|
18 |
+
## How to use
|
19 |
+
|
20 |
+
```
|
21 |
+
Click: select body
|
22 |
+
Left Drag: move joint (on joint)
|
23 |
+
rotate camera (otherwise)
|
24 |
+
Right Drag: move whole body (if selected)
|
25 |
+
move camera (otherwise)
|
26 |
+
Wheel: zoom
|
27 |
+
```
|
28 |
+
|
29 |
+
## Installation
|
30 |
+
|
31 |
+
- [Web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) Extension
|
32 |
+
- Online (Github Pages)
|
33 |
+
- Standalone
|
34 |
+
|
35 |
+
### [Web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) Extension
|
36 |
+
|
37 |
+
**Prerequirement: You need to install [Mikubill/sd-webui-controlnet](https://github.com/Mikubill/sd-webui-controlnet) to use Posex with ControlNet Pose2Img.**
|
38 |
+
|
39 |
+
Go to `Extensions` tab, then select `Install from URL` tab and input `https://github.com/hnmr293/posex`.
|
40 |
+
|
41 |
+
Or move to `extensions` directory and type `git clone https://github.com/hnmr293/posex`.
|
42 |
+
|
43 |
+
In webui, open `Posex` accordion in `txt2img` or `img2img` tab, then click a checkbox. The canvas will be opened.
|
44 |
+
|
45 |
+
### Online
|
46 |
+
|
47 |
+
See Github Pages.
|
48 |
+
|
49 |
+
[https://hnmr293.github.io/posex](https://hnmr293.github.io/posex)
|
50 |
+
|
51 |
+
### Standalone
|
52 |
+
|
53 |
+
```
|
54 |
+
$ pip install -r requirements.txt
|
55 |
+
$ python app.py
|
56 |
+
```
|
57 |
+
|
58 |
+
Then open `localhost:55502` or `127.0.0.1:55502` in your browser.
|
59 |
+
|
60 |
+
## Example
|
61 |
+
|
62 |
+
### Web UI
|
63 |
+
|
64 |
+
sample 1:
|
65 |
+
|
66 |
+
![sample](./image/sample-webui.png)
|
67 |
+
|
68 |
+
result:
|
69 |
+
|
70 |
+
![sample result](./image/sample-webui2.jpg)
|
71 |
+
|
72 |
+
sample 2:
|
73 |
+
|
74 |
+
![sample 2](./saved_poses/bridge.png)
|
75 |
+
|
76 |
+
result:
|
77 |
+
|
78 |
+
![sample 2 result](./image/sample-webui3.png)
|
79 |
+
|
80 |
+
### Standalone
|
81 |
+
|
82 |
+
![sample](./image/sample.png)
|
83 |
+
|
84 |
+
## History
|
85 |
+
|
86 |
+
### v0.3 -> v0.4 features
|
87 |
+
- background setting
|
88 |
+
- save/load pose
|
89 |
+
- fixed camera roll
|
90 |
+
|
91 |
+
### v0.2 -> v0.3 features
|
92 |
+
- Web UI extension
|
93 |
+
|
94 |
+
### v0.1 -> v0.2 features
|
95 |
+
- copying the image to clipboard
|
96 |
+
- multiple bodies
|
97 |
+
- canvas size changing
|
98 |
+
- UI has become ugly >_<;
|
posex/app.py
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Union
|
2 |
+
|
3 |
+
def ensure_install(module_name: str, lib_name: Union[str,None] = None):
|
4 |
+
from importlib.util import find_spec
|
5 |
+
|
6 |
+
if lib_name is None:
|
7 |
+
lib_name = module_name
|
8 |
+
|
9 |
+
if find_spec(module_name) is None:
|
10 |
+
import subprocess
|
11 |
+
try:
|
12 |
+
print('-' * 80, file=sys.stderr)
|
13 |
+
print(f'| installing {lib_name} ...', file=sys.stderr)
|
14 |
+
print('-' * 80, file=sys.stderr)
|
15 |
+
subprocess.check_call(
|
16 |
+
[sys.executable, "-m", "pip", "install", lib_name],
|
17 |
+
stdout=sys.stdout,
|
18 |
+
stderr=sys.stderr
|
19 |
+
)
|
20 |
+
except Exception as e:
|
21 |
+
msg = ''.join(traceback.TracebackException.from_exception(e).format())
|
22 |
+
print(msg, file=sys.stderr)
|
23 |
+
print('-' * 80, file=sys.stderr)
|
24 |
+
print(f'| failed to install {lib_name}. exit...', file=sys.stderr)
|
25 |
+
print('-' * 80, file=sys.stderr)
|
26 |
+
sys.exit(1)
|
27 |
+
|
28 |
+
if __name__ == '__main__':
|
29 |
+
import mimetypes
|
30 |
+
mimetypes.add_type('application/javascript', '.js')
|
31 |
+
|
32 |
+
import os, sys, traceback
|
33 |
+
from functools import wraps
|
34 |
+
|
35 |
+
ensure_install('flask')
|
36 |
+
ensure_install('PIL', 'pillow')
|
37 |
+
|
38 |
+
from flask import Flask, jsonify, request
|
39 |
+
from common import posex_utils as posex
|
40 |
+
|
41 |
+
app = Flask(__name__, static_folder='.', static_url_path='')
|
42 |
+
|
43 |
+
posex.set_save_dir(os.path.join(app.static_folder, 'saved_poses'))
|
44 |
+
|
45 |
+
@app.route('/')
|
46 |
+
def index():
|
47 |
+
return app.send_static_file('index.html')
|
48 |
+
|
49 |
+
def api_try(fn):
|
50 |
+
@wraps(fn)
|
51 |
+
def f(*args, **kwargs):
|
52 |
+
try:
|
53 |
+
return fn(*args, **kwargs)
|
54 |
+
except Exception as e:
|
55 |
+
msg = ''.join(traceback.TracebackException.from_exception(e).format())
|
56 |
+
print(msg, file=sys.stderr)
|
57 |
+
return jsonify(result=str(e), ok=False)
|
58 |
+
return f
|
59 |
+
|
60 |
+
@app.route('/pose/all')
|
61 |
+
@api_try
|
62 |
+
def all_poses():
|
63 |
+
return jsonify(list(posex.all_poses()))
|
64 |
+
|
65 |
+
@app.route('/pose/save', methods=['POST'])
|
66 |
+
@api_try
|
67 |
+
def save_pose():
|
68 |
+
data = request.json
|
69 |
+
posex.save_pose(data)
|
70 |
+
return jsonify(result='pose saved', ok=True)
|
71 |
+
|
72 |
+
@app.route('/pose/delete', methods=['POST'])
|
73 |
+
@api_try
|
74 |
+
def delete_pose():
|
75 |
+
data = request.json
|
76 |
+
posex.delete_pose(data['name'])
|
77 |
+
return jsonify(result='pose deleted', ok=True)
|
78 |
+
|
79 |
+
@app.route('/pose/load', methods=['POST'])
|
80 |
+
@api_try
|
81 |
+
def load_pose():
|
82 |
+
data = request.json
|
83 |
+
return jsonify(posex.load_pose(data['name']))
|
84 |
+
|
85 |
+
app.run(port=55502, debug=True)
|
posex/common/__pycache__/posex_utils.cpython-310.pyc
ADDED
Binary file (3.67 kB). View file
|
|
posex/common/posex_utils.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os, glob, json, base64, re
|
2 |
+
from io import BytesIO
|
3 |
+
from PIL import Image, PngImagePlugin
|
4 |
+
|
5 |
+
_SAVED_POSES_DIR = ''
|
6 |
+
|
7 |
+
def set_save_dir(dir: str):
|
8 |
+
global _SAVED_POSES_DIR
|
9 |
+
_SAVED_POSES_DIR = os.path.realpath(str(dir))
|
10 |
+
|
11 |
+
def get_save_dir():
|
12 |
+
assert len(_SAVED_POSES_DIR) != 0
|
13 |
+
return _SAVED_POSES_DIR
|
14 |
+
|
15 |
+
def get_saved_path(name: str):
|
16 |
+
#return os.path.realpath(os.path.join(get_save_dir(), name))
|
17 |
+
return os.path.join(get_save_dir(), name)
|
18 |
+
|
19 |
+
def atoi(text):
|
20 |
+
return int(text) if text.isdigit() else text
|
21 |
+
|
22 |
+
def natural_keys(text):
|
23 |
+
return [ atoi(c) for c in re.split(r'(\d+)', text) ]
|
24 |
+
|
25 |
+
def sorted_glob(path):
|
26 |
+
return sorted(glob.glob(path), key=natural_keys)
|
27 |
+
|
28 |
+
def name2path(name: str):
|
29 |
+
if not isinstance(name, str):
|
30 |
+
raise ValueError(f'str object expected, but {type(name)}')
|
31 |
+
|
32 |
+
if len(name) == 0:
|
33 |
+
raise ValueError(f'empty name')
|
34 |
+
|
35 |
+
if '.' in name or '/' in name or '\\' in name:
|
36 |
+
raise ValueError(f'invalid name: {name}')
|
37 |
+
|
38 |
+
path = get_saved_path(f'{name}.png')
|
39 |
+
if not path.startswith(get_save_dir()):
|
40 |
+
raise ValueError(f'invalid name: {name}')
|
41 |
+
|
42 |
+
return path
|
43 |
+
|
44 |
+
def saved_poses():
|
45 |
+
for path in sorted_glob(os.path.join(get_save_dir(), '*.png')):
|
46 |
+
yield Image.open(path)
|
47 |
+
|
48 |
+
def all_poses():
|
49 |
+
for img in saved_poses():
|
50 |
+
buffer = BytesIO()
|
51 |
+
img.save(buffer, format='png')
|
52 |
+
|
53 |
+
if not hasattr(img, 'text'):
|
54 |
+
continue
|
55 |
+
|
56 |
+
pose_dict = {
|
57 |
+
'name': img.text['name'], # type: ignore
|
58 |
+
'image': base64.b64encode(buffer.getvalue()).decode('ascii'),
|
59 |
+
'screen': json.loads(img.text['screen']), # type: ignore
|
60 |
+
'camera': json.loads(img.text['camera']), # type: ignore
|
61 |
+
'joints': json.loads(img.text['joints']), # type: ignore
|
62 |
+
}
|
63 |
+
|
64 |
+
yield pose_dict
|
65 |
+
|
66 |
+
def save_pose(data: dict):
|
67 |
+
name = data['name']
|
68 |
+
screen = data['screen']
|
69 |
+
camera = data['camera']
|
70 |
+
joints = data['joints']
|
71 |
+
|
72 |
+
info = PngImagePlugin.PngInfo()
|
73 |
+
info.add_text('name', name)
|
74 |
+
info.add_text('screen', json.dumps(screen))
|
75 |
+
info.add_text('camera', json.dumps(camera))
|
76 |
+
info.add_text('joints', json.dumps(joints))
|
77 |
+
|
78 |
+
filepath = name2path(name)
|
79 |
+
|
80 |
+
image = Image.open(BytesIO(base64.b64decode(data['image'][len('data:image/png;base64,'):])))
|
81 |
+
unit = max(image.width, image.height)
|
82 |
+
mx, my = (unit - image.width) // 2, (unit - image.height) // 2
|
83 |
+
canvas = Image.new('RGB', (unit, unit), color=(68, 68, 68))
|
84 |
+
canvas.paste(image, (mx, my))
|
85 |
+
image = canvas.resize((canvas.width//4, canvas.height//4))
|
86 |
+
|
87 |
+
image.save(filepath, pnginfo=info)
|
88 |
+
|
89 |
+
def delete_pose(name: str):
|
90 |
+
filepath = name2path(name)
|
91 |
+
os.remove(filepath)
|
92 |
+
|
93 |
+
def load_pose(name: str):
|
94 |
+
filepath = name2path(name)
|
95 |
+
img = Image.open(filepath)
|
96 |
+
|
97 |
+
buffer = BytesIO()
|
98 |
+
img.save(buffer, format='png')
|
99 |
+
|
100 |
+
if not hasattr(img, 'text'):
|
101 |
+
raise ValueError(f'not pose data: {filepath}')
|
102 |
+
|
103 |
+
pose_dict = {
|
104 |
+
'name': img.text['name'], # type: ignore
|
105 |
+
'image': base64.b64encode(buffer.getvalue()).decode('ascii'),
|
106 |
+
'screen': json.loads(img.text['screen']), # type: ignore
|
107 |
+
'camera': json.loads(img.text['camera']), # type: ignore
|
108 |
+
'joints': json.loads(img.text['joints']), # type: ignore
|
109 |
+
}
|
110 |
+
|
111 |
+
return pose_dict
|
posex/css/main.css
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#cont {
|
2 |
+
display: flex;
|
3 |
+
flex-flow: column;
|
4 |
+
gap: 0.5em 0;
|
5 |
+
width: max-content;
|
6 |
+
}
|
7 |
+
|
8 |
+
.box {
|
9 |
+
display: block;
|
10 |
+
border: 1px solid gray;
|
11 |
+
padding: 0.5em 0;
|
12 |
+
text-decoration: none;
|
13 |
+
text-align: center;
|
14 |
+
}
|
15 |
+
|
16 |
+
#notation {
|
17 |
+
display: none;
|
18 |
+
position: absolute;
|
19 |
+
color: black;
|
20 |
+
background-color: rgba(255, 255, 255, 0.75);
|
21 |
+
padding: 0.1em 0.25em;
|
22 |
+
pointer-events: none;
|
23 |
+
font-size: small;
|
24 |
+
}
|
25 |
+
|
26 |
+
#notifications {
|
27 |
+
left: 1em;
|
28 |
+
bottom: 0;
|
29 |
+
width: 25%;
|
30 |
+
}
|
31 |
+
|
32 |
+
#notifications .item {
|
33 |
+
padding: 0.5em;
|
34 |
+
border: 1px solid gray;
|
35 |
+
animation: erase 2s ease-out 0.5s forwards;
|
36 |
+
}
|
37 |
+
|
38 |
+
@keyframes erase {
|
39 |
+
100% {
|
40 |
+
opacity: 0;
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
#notifications .success {
|
45 |
+
background-color: #aaccaa;
|
46 |
+
color: #004000;
|
47 |
+
}
|
48 |
+
|
49 |
+
#notifications .info {
|
50 |
+
background-color: white;
|
51 |
+
color: black;
|
52 |
+
}
|
53 |
+
|
54 |
+
#notifications .error {
|
55 |
+
background-color: #ffcccc;
|
56 |
+
color: red;
|
57 |
+
}
|
58 |
+
|
59 |
+
#body_indicator1 {
|
60 |
+
display: none;
|
61 |
+
position: absolute;
|
62 |
+
outline: 1px solid white;
|
63 |
+
pointer-events: none;
|
64 |
+
}
|
65 |
+
|
66 |
+
#body_indicator2 {
|
67 |
+
display: none;
|
68 |
+
position: absolute;
|
69 |
+
outline: 1px solid gray;
|
70 |
+
pointer-events: none;
|
71 |
+
}
|
72 |
+
|
73 |
+
#saved_poses {
|
74 |
+
display: flex;
|
75 |
+
flex-direction: row;
|
76 |
+
gap: 0.25em;
|
77 |
+
font-size: small;
|
78 |
+
}
|
79 |
+
|
80 |
+
#saved_poses > * {
|
81 |
+
margin: 0;
|
82 |
+
outline: 1px solid gray;
|
83 |
+
max-width: 128px;
|
84 |
+
}
|
85 |
+
|
86 |
+
#saved_poses img {
|
87 |
+
max-width: 128px;
|
88 |
+
max-height: 128px;
|
89 |
+
}
|
90 |
+
|
91 |
+
#saved_poses figcaption {
|
92 |
+
padding: 0 0.25em;
|
93 |
+
overflow-wrap: anywhere; /* Opera Android may not be able to interpret `anywhere` keyword. */
|
94 |
+
}
|
95 |
+
|
96 |
+
#saved_poses .close {
|
97 |
+
position: absolute;
|
98 |
+
cursor: pointer;
|
99 |
+
border: 1px solid gray;
|
100 |
+
background-color: white;
|
101 |
+
opacity: 0.5;
|
102 |
+
width: 1.25em;
|
103 |
+
height: 1.25em;
|
104 |
+
text-align: center;
|
105 |
+
vertical-align: middle;
|
106 |
+
margin: 0;
|
107 |
+
font-family: monospace;
|
108 |
+
}
|
109 |
+
|
110 |
+
#saved_poses .close:hover {
|
111 |
+
opacity: 1.0;
|
112 |
+
}
|
113 |
+
|
114 |
+
#saved_poses .close2 {
|
115 |
+
display: none;
|
116 |
+
position: absolute;
|
117 |
+
left: 1.5em;
|
118 |
+
top: 0;
|
119 |
+
}
|
120 |
+
|
121 |
+
#saved_poses .close:hover .close2 {
|
122 |
+
display: block;
|
123 |
+
color: white;
|
124 |
+
pointer-events: none;
|
125 |
+
}
|
posex/image/favicon.ico
ADDED
posex/image/favicon.png
ADDED
posex/image/sample-webui.png
ADDED
posex/image/sample-webui2.jpg
ADDED
Git LFS Details
|
posex/image/sample-webui2.png
ADDED
Git LFS Details
|
posex/image/sample-webui3.png
ADDED
posex/image/sample.png
ADDED
posex/index.html
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<title>Posex - Estimated Image Generator for Pose2Image</title>
|
6 |
+
<link rel="icon" type="image/png" href="image/favicon.png">
|
7 |
+
<link rel="stylesheet" href="css/main.css">
|
8 |
+
<script async src="js/es-module-shims.js"></script>
|
9 |
+
<script type="importmap">
|
10 |
+
{
|
11 |
+
"imports": {
|
12 |
+
"three": "./js/three.module.js",
|
13 |
+
"three-trackballcontrols": "./js/TrackballControls.js",
|
14 |
+
"three-dragcontrols": "./js/DragControls.js",
|
15 |
+
"three-meshline": "./js/THREE.MeshLine.Module.min.js",
|
16 |
+
"posex": "./js/posex.js"
|
17 |
+
}
|
18 |
+
}
|
19 |
+
</script>
|
20 |
+
<script type="esms-options">
|
21 |
+
{
|
22 |
+
"noLoadEventRetriggers": true
|
23 |
+
}
|
24 |
+
</script>
|
25 |
+
<script defer type="module" src="js/app.js"></script>
|
26 |
+
</head>
|
27 |
+
|
28 |
+
<body>
|
29 |
+
<div id="cont">
|
30 |
+
<button id="all_reset" class="box">🔄 All Reset</button>
|
31 |
+
<div style="display: flex; flex-direction: row; gap: 0 0.5em;">
|
32 |
+
<button id="reset_camera" class="box" style="flex: 1 1 0">🎥 Reset Camera</button>
|
33 |
+
<button id="reset_pose" class="box" style="flex: 1 1 0">🧍 Reset Pose</button>
|
34 |
+
</div>
|
35 |
+
<div style="display: flex; flex-direction: row; ">
|
36 |
+
|
37 |
+
<canvas width="512" height="512" id="main_canvas"></canvas>
|
38 |
+
|
39 |
+
<div style="margin-left: 1em; display: flex; flex-direction: column; gap: 0.25em; font-size: smaller;">
|
40 |
+
<div>- Camera</div>
|
41 |
+
<label><input type="checkbox" id="fixed_roll">Fixed Roll</label>
|
42 |
+
<div>- Body</div>
|
43 |
+
<button id="add_body" style="width: max-content; min-width: 100%; text-align: left;">➕ Add</button>
|
44 |
+
<button id="remove_body" style="width: max-content; min-width: 100%; text-align: left;">➖ Remove</button>
|
45 |
+
<div>- Canvas Size</div>
|
46 |
+
<input type="number" id="canvas_width" value="512" min="64" />
|
47 |
+
<input type="number" id="canvas_height" value="512" min="64" />
|
48 |
+
<div>- Background</div>
|
49 |
+
<div style="display: flex; flex-direction: row; gap: 0 0.25em;">
|
50 |
+
<label style="flex: 0 0 0;"><button onclick="document.querySelector('#bg_file').click();">🖼 Set</button><input type="file" id="bg_file" style="display: none;"/></label>
|
51 |
+
<button id="reset_bg" style="flex: 0 0 0;">❌ Del</button>
|
52 |
+
</div>
|
53 |
+
<div>- Joints and Limbs</div>
|
54 |
+
<!-- <label><input type="range" id="joint_radius" min="1" max="16" value="4" />Joint Radius</label> -->
|
55 |
+
<label><input type="range" id="limb_width" min="1" max="16" value="4" />Limb Width</label>
|
56 |
+
<label><input type="checkbox" id="elliptic_limbs" checked />Elliptic Limbs</label>
|
57 |
+
<div>- Others</div>
|
58 |
+
<label><input type="checkbox" id="low_fps" />Low fps</label>
|
59 |
+
</div>
|
60 |
+
</div>
|
61 |
+
<div id="body_indicator2"></div>
|
62 |
+
<div id="body_indicator1"></div>
|
63 |
+
<div style="display: flex; flex-direction: row; gap: 0 0.25em;">
|
64 |
+
<a id="save_button" class="box" style="flex: 1 1 0;" href="#">💾 Save Image</a>
|
65 |
+
<a id="copy_button" class="box" style="flex: 1 1 0;" href="#">📋 Copy to clipboard</a>
|
66 |
+
</div>
|
67 |
+
<button id="save_pose" class="box">💾🧍 Save Pose</button>
|
68 |
+
<div id="saved_poses"></div>
|
69 |
+
</div>
|
70 |
+
<p id="notation"></p>
|
71 |
+
<div id="notifications"></div>
|
72 |
+
</body>
|
73 |
+
|
74 |
+
</html>
|
posex/javascript/lazyload/posex-webui.js
ADDED
@@ -0,0 +1,386 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
async function _import() {
|
2 |
+
if (!globalThis.posex || !globalThis.posex.import) {
|
3 |
+
return await import('posex');
|
4 |
+
} else {
|
5 |
+
return await globalThis.posex.imports.posex();
|
6 |
+
}
|
7 |
+
}
|
8 |
+
const { init, init_3d } = await _import();
|
9 |
+
|
10 |
+
(async function () {
|
11 |
+
let _r = 0;
|
12 |
+
function to_gradio(v) {
|
13 |
+
// force call `change` event on gradio
|
14 |
+
return [v.toString(), (_r++).toString()];
|
15 |
+
}
|
16 |
+
|
17 |
+
function js2py(type, gradio_field, value) {
|
18 |
+
// set `value` to gradio's field
|
19 |
+
// (1) Click gradio's button.
|
20 |
+
// (2) Gradio will fire js callback to retrieve value to be set.
|
21 |
+
// (3) Gradio will fire another js callback to notify the process has been completed.
|
22 |
+
return new Promise(resolve => {
|
23 |
+
const callback_name = `posex-${type}-${gradio_field}`;
|
24 |
+
|
25 |
+
// (2)
|
26 |
+
globalThis[callback_name] = () => {
|
27 |
+
|
28 |
+
delete globalThis[callback_name];
|
29 |
+
|
30 |
+
// (3)
|
31 |
+
const callback_after = callback_name + '_after';
|
32 |
+
globalThis[callback_after] = () => {
|
33 |
+
delete globalThis[callback_after];
|
34 |
+
resolve();
|
35 |
+
};
|
36 |
+
|
37 |
+
return to_gradio(value);
|
38 |
+
};
|
39 |
+
|
40 |
+
// (1)
|
41 |
+
gradioApp().querySelector(`#${callback_name}_set`).click();
|
42 |
+
});
|
43 |
+
}
|
44 |
+
|
45 |
+
function py2js(type, pyname, ...args) {
|
46 |
+
// call python's function
|
47 |
+
// (1) Set args to gradio's field
|
48 |
+
// (2) Click gradio's button
|
49 |
+
// (3) JS callback will be kicked with return value from gradio
|
50 |
+
|
51 |
+
// (1)
|
52 |
+
return (args.length == 0 ? Promise.resolve() : js2py(type, pyname + '_args', JSON.stringify(args)))
|
53 |
+
.then(() => {
|
54 |
+
return new Promise(resolve => {
|
55 |
+
const callback_name = `posex-${type}-${pyname}`;
|
56 |
+
// (3)
|
57 |
+
globalThis[callback_name] = value => {
|
58 |
+
delete globalThis[callback_name];
|
59 |
+
resolve(value);
|
60 |
+
}
|
61 |
+
// (2)
|
62 |
+
gradioApp().querySelector(`#${callback_name}_get`).click();
|
63 |
+
});
|
64 |
+
});
|
65 |
+
}
|
66 |
+
|
67 |
+
function reload_poses(json, ui) {
|
68 |
+
const df = document.createDocumentFragment();
|
69 |
+
for (let data of json) {
|
70 |
+
const fig = document.createElement('figure')
|
71 |
+
const img = document.createElement('img');
|
72 |
+
const cap = document.createElement('figcaption');
|
73 |
+
const clo = document.createElement('div');
|
74 |
+
const cloimg = document.createElement('img');
|
75 |
+
const clo2 = document.createElement('span');
|
76 |
+
fig.dataset.poseName = data.name;
|
77 |
+
cap.textContent = data.name;
|
78 |
+
clo.classList.add('close');
|
79 |
+
cloimg.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAAAG3RFWHRTb2Z0d2FyZQBDZWxzeXMgU3R1ZGlvIFRvb2zBp+F8AAAAxElEQVQ4y82T0REBQRBEX0dAJk4GJwKXgZMBESADIiADJwKXATI4GRBB+1lqKa62+DFV+7E7U6+6e2plm19K/wWQ1AE2QBcobZ9fehVwCb3rWwWShmHwaLsfvW+BAihs71otSJoBc2BuexFBl7anSRlI2gN5OBXQxIpSAB2gCXlcgCzOJGkLkg5ABhyB/B5cqoI1UAb5BbCxPU4CSBqFdda2B1GoE9urVoCkHlCH68N3ZOfzGkNw9dvBZ3Bu+/SHf+GbugG9/4ThhKqF8gAAAABJRU5ErkJggg==';
|
80 |
+
clo2.classList.add('close2');
|
81 |
+
clo2.textContent = 'delete';
|
82 |
+
clo.append(cloimg, clo2);
|
83 |
+
|
84 |
+
img.src = 'data:image/png;base64,' + data.image;
|
85 |
+
img.title = data.name;
|
86 |
+
fig.append(clo, img, cap);
|
87 |
+
|
88 |
+
df.appendChild(fig);
|
89 |
+
}
|
90 |
+
|
91 |
+
ui.saved_poses.innerHTML = '';
|
92 |
+
ui.saved_poses.appendChild(df);
|
93 |
+
}
|
94 |
+
|
95 |
+
function init_ui(type, api) {
|
96 |
+
const $ = x => document.createElement(x);
|
97 |
+
|
98 |
+
const all_reset = $('button');
|
99 |
+
all_reset.innerHTML = '🔄 All Reset';
|
100 |
+
all_reset.classList.add('posex_all_reset', 'posex_box');
|
101 |
+
|
102 |
+
const reset_camera = $('button');
|
103 |
+
reset_camera.innerHTML = '🎥 Reset Camera';
|
104 |
+
reset_camera.classList.add('posex_reset_camera', 'posex_box');
|
105 |
+
|
106 |
+
const reset_pose = $('button');
|
107 |
+
reset_pose.innerHTML = '🧍 Reset Pose';
|
108 |
+
reset_pose.classList.add('posex_reset_pose', 'posex_box');
|
109 |
+
|
110 |
+
const reset_cont = $('div');
|
111 |
+
reset_cont.classList.add('posex_reset_cont');
|
112 |
+
reset_cont.append(reset_camera, reset_pose);
|
113 |
+
|
114 |
+
const canvas = $('canvas');
|
115 |
+
canvas.width = 512;
|
116 |
+
canvas.height = 512;
|
117 |
+
|
118 |
+
const camera_marker = $('div'); camera_marker.textContent = '- Camera';
|
119 |
+
const fixed_roll_label = $('label');
|
120 |
+
const fixed_roll = $('input'); fixed_roll.type = 'checkbox'; fixed_roll.classList.add('posex_fixed_roll', 'posex_camera'); fixed_roll.checked = true;
|
121 |
+
fixed_roll_label.append(fixed_roll, document.createTextNode('Fixed Roll'));
|
122 |
+
const body_marker = $('div'); body_marker.textContent = '- Body';
|
123 |
+
const add_body = $('button'); add_body.classList.add('posex_add_body', 'posex_body'); add_body.innerHTML = '➕ Add';
|
124 |
+
const remove_body = $('button'); remove_body.classList.add('posex_remove_body', 'posex_body'); remove_body.innerHTML = '➖ Remove';
|
125 |
+
const canvas_marker = $('div'); canvas_marker.textContent = '- Canvas Size';
|
126 |
+
const canvas_width = $('input'); canvas_width.type = 'number'; canvas_width.value = 512; canvas_width.min = 64; canvas_width.classList.add('posex_canvas_width', 'posex_canvas_size');
|
127 |
+
const canvas_height = $('input'); canvas_height.type = 'number'; canvas_height.value = 512; canvas_height.min = 64; canvas_height.classList.add('posex_canvas_height', 'posex_canvas_size');
|
128 |
+
const bg_marker = $('div'); bg_marker.textContent = '- Background';
|
129 |
+
const set_bg = $('label'); set_bg.classList.add('posex_bg');
|
130 |
+
const bg_button = $('button'); bg_button.innerHTML = '🖼 Set'; bg_button.onclick = () => bg_input.click();
|
131 |
+
const bg_input = $('input'); bg_input.type = 'file'; bg_input.style.display = 'none';
|
132 |
+
set_bg.append(bg_button, bg_input);
|
133 |
+
const reset_bg = $('button'); reset_bg.classList.add('posex_bg'); reset_bg.innerHTML = '❌ Del';
|
134 |
+
const bg_cont = $('div'); bg_cont.classList.add('posex_bg_cont');
|
135 |
+
bg_cont.append(set_bg, reset_bg);
|
136 |
+
const joint_marker = $('div'); joint_marker.textContent = '- Joints and Limbs';
|
137 |
+
const limb_width_label = $('label');
|
138 |
+
const limb_width = $('input'); limb_width.type = 'range'; limb_width.min = 1; limb_width.max = 16; limb_width.value = 4; limb_width.classList.add('posex_joints', 'posex_limb_width');
|
139 |
+
limb_width_label.append(limb_width, document.createTextNode('Limb Width'));
|
140 |
+
const elliptic_limbs_label = $('label');
|
141 |
+
const elliptic_limbs = $('input'); elliptic_limbs.type = 'checkbox'; elliptic_limbs.classList.add('posex_joints', 'posex_elliptic_limbs'); elliptic_limbs.checked = true;
|
142 |
+
elliptic_limbs_label.append(elliptic_limbs, document.createTextNode('Elliptic Limbs'));
|
143 |
+
const other_marker = $('div'); other_marker.textContent = '- Others';
|
144 |
+
const low_fps_label = $('label');
|
145 |
+
const low_fps = $('input'); low_fps.type = 'checkbox'; low_fps.classList.add('posex_low_fps', 'posex_others'); low_fps.checked = false;
|
146 |
+
low_fps_label.append(low_fps, document.createTextNode('Low fps'));
|
147 |
+
|
148 |
+
const setting_cont = $('div');
|
149 |
+
setting_cont.classList.add('posex_setting_cont');
|
150 |
+
setting_cont.append(
|
151 |
+
camera_marker,
|
152 |
+
fixed_roll_label,
|
153 |
+
body_marker,
|
154 |
+
add_body,
|
155 |
+
remove_body,
|
156 |
+
canvas_marker,
|
157 |
+
canvas_width,
|
158 |
+
canvas_height,
|
159 |
+
bg_marker,
|
160 |
+
bg_cont,
|
161 |
+
joint_marker,
|
162 |
+
limb_width_label,
|
163 |
+
elliptic_limbs_label,
|
164 |
+
other_marker,
|
165 |
+
low_fps_label,
|
166 |
+
);
|
167 |
+
|
168 |
+
const canvas_cont = $('div');
|
169 |
+
canvas_cont.classList.add('posex_canvas_cont');
|
170 |
+
canvas_cont.append(
|
171 |
+
canvas,
|
172 |
+
setting_cont,
|
173 |
+
);
|
174 |
+
|
175 |
+
const notation = $('p');
|
176 |
+
notation.classList.add('posex_notation');
|
177 |
+
|
178 |
+
const indicator1 = $('div');
|
179 |
+
indicator1.classList.add('posex_indicator1');
|
180 |
+
|
181 |
+
const indicator2 = $('div');
|
182 |
+
indicator2.classList.add('posex_indicator2');
|
183 |
+
|
184 |
+
const copy = $('button'); copy.classList.add('posex_copy', 'posex_misc', 'posex_box'); copy.innerHTML = '📋 Copy to clipboard';
|
185 |
+
const save = $('button'); save.classList.add('posex_save', 'posex_misc', 'posex_box'); save.innerHTML = '💾 Download image';
|
186 |
+
|
187 |
+
const misc_cont = $('div');
|
188 |
+
misc_cont.classList.add('posex_misc_cont');
|
189 |
+
misc_cont.append(
|
190 |
+
copy,
|
191 |
+
save
|
192 |
+
);
|
193 |
+
|
194 |
+
const save_pose = $('button');
|
195 |
+
save_pose.classList.add('posex_save_pose', 'posex_box');
|
196 |
+
save_pose.innerHTML = '💾🧍 Save Pose';
|
197 |
+
|
198 |
+
const save_pose_callback = async obj => {
|
199 |
+
await py2js(type, 'savepose', obj);
|
200 |
+
const json = await py2js(type, 'allposes')
|
201 |
+
reload_poses(JSON.parse(json), ui);
|
202 |
+
return { result: '', ok: true };
|
203 |
+
};
|
204 |
+
|
205 |
+
const saved_poses = $('div');
|
206 |
+
saved_poses.classList.add('posex_saved_poses');
|
207 |
+
|
208 |
+
saved_poses.addEventListener('click', async e => {
|
209 |
+
const get_name = ele => {
|
210 |
+
while (ele && ele !== document) {
|
211 |
+
if (ele.dataset && ele.dataset.poseName !== undefined)
|
212 |
+
return ele.dataset.poseName;
|
213 |
+
ele = ele.parentNode;
|
214 |
+
}
|
215 |
+
return '';
|
216 |
+
};
|
217 |
+
|
218 |
+
let target = e.target;
|
219 |
+
if (target.tagName === 'IMG') target = target.parentNode;
|
220 |
+
if (target.classList.contains('close2')) target = target.parentNode;
|
221 |
+
if (target.tagName === 'FIGURE') {
|
222 |
+
const name = get_name(target);
|
223 |
+
if (name.length != 0) {
|
224 |
+
const json = await py2js(type, 'loadpose', name);
|
225 |
+
ui.loadPose(JSON.parse(json));
|
226 |
+
}
|
227 |
+
} else if (target.classList.contains('close')) {
|
228 |
+
const name = get_name(target);
|
229 |
+
if (name.length != 0) {
|
230 |
+
await py2js(type, 'delpose', name);
|
231 |
+
const json = await py2js(type, 'allposes')
|
232 |
+
reload_poses(JSON.parse(json), ui);
|
233 |
+
}
|
234 |
+
}
|
235 |
+
}, false);
|
236 |
+
|
237 |
+
const ui = {
|
238 |
+
canvas,
|
239 |
+
notation,
|
240 |
+
indicator1,
|
241 |
+
indicator2,
|
242 |
+
all_reset,
|
243 |
+
reset_camera,
|
244 |
+
reset_pose,
|
245 |
+
fixed_roll,
|
246 |
+
add_body,
|
247 |
+
remove_body,
|
248 |
+
canvas_width,
|
249 |
+
canvas_height,
|
250 |
+
bg: bg_input,
|
251 |
+
reset_bg,
|
252 |
+
limb_width,
|
253 |
+
elliptic_limbs,
|
254 |
+
low_fps,
|
255 |
+
save,
|
256 |
+
copy,
|
257 |
+
save_pose,
|
258 |
+
save_pose_callback,
|
259 |
+
saved_poses,
|
260 |
+
};
|
261 |
+
|
262 |
+
const df = document.createDocumentFragment();
|
263 |
+
df.append(
|
264 |
+
all_reset,
|
265 |
+
reset_cont,
|
266 |
+
canvas_cont,
|
267 |
+
indicator2,
|
268 |
+
indicator1,
|
269 |
+
notation,
|
270 |
+
misc_cont,
|
271 |
+
save_pose,
|
272 |
+
saved_poses,
|
273 |
+
);
|
274 |
+
|
275 |
+
return { ui, df };
|
276 |
+
};
|
277 |
+
|
278 |
+
async function init_canvas(
|
279 |
+
type,
|
280 |
+
enabled,
|
281 |
+
generate_button,
|
282 |
+
container,
|
283 |
+
api
|
284 |
+
) {
|
285 |
+
container.classList.add('posex_cont');
|
286 |
+
container.innerHTML = '';
|
287 |
+
const { ui, df } = init_ui(type, api);
|
288 |
+
container.appendChild(df);
|
289 |
+
|
290 |
+
ui.container = container;
|
291 |
+
ui.notify = function (str, type) { if (type === 'error') console.error(str); };
|
292 |
+
|
293 |
+
if (!posex[`${type}_click`]) {
|
294 |
+
// Send canvas image to ControlNet when button is clicked.
|
295 |
+
let force = false;
|
296 |
+
gradioApp().addEventListener('click', async e => {
|
297 |
+
if (e.target !== generate_button) return;
|
298 |
+
|
299 |
+
if (!enabled.checked) return;
|
300 |
+
|
301 |
+
if (force) {
|
302 |
+
force = false;
|
303 |
+
return;
|
304 |
+
}
|
305 |
+
|
306 |
+
// hook `generate` button to add canvas data
|
307 |
+
e.preventDefault();
|
308 |
+
e.stopPropagation();
|
309 |
+
|
310 |
+
const data_url = await ui.getDataURL();
|
311 |
+
await js2py(type, 'base64', data_url);
|
312 |
+
force = true;
|
313 |
+
generate_button.click();
|
314 |
+
}, true);
|
315 |
+
posex[`${type}_click`] = true;
|
316 |
+
}
|
317 |
+
|
318 |
+
{
|
319 |
+
// Load saved poses.
|
320 |
+
const json = await py2js(type, 'allposes')
|
321 |
+
reload_poses(JSON.parse(json), ui);
|
322 |
+
}
|
323 |
+
|
324 |
+
init(ui);
|
325 |
+
|
326 |
+
const animate = init_3d(ui);
|
327 |
+
|
328 |
+
animate();
|
329 |
+
|
330 |
+
onUiTabChange(() => {
|
331 |
+
const tabname = get_uiCurrentTabContent().id;
|
332 |
+
if (type === 't2i') {
|
333 |
+
if (0 <= tabname.indexOf('txt2img')) {
|
334 |
+
ui.play();
|
335 |
+
} else {
|
336 |
+
ui.stop();
|
337 |
+
}
|
338 |
+
} else if (type === 'i2i') {
|
339 |
+
if (0 <= tabname.indexOf('img2img')) {
|
340 |
+
ui.play();
|
341 |
+
} else {
|
342 |
+
ui.stop();
|
343 |
+
}
|
344 |
+
} else {
|
345 |
+
ui.stop();
|
346 |
+
}
|
347 |
+
});
|
348 |
+
}
|
349 |
+
|
350 |
+
async function init_t2i() {
|
351 |
+
const app = gradioApp();
|
352 |
+
await init_canvas(
|
353 |
+
't2i',
|
354 |
+
app.querySelector('#posex-t2i-enabled input[type=checkbox]'),
|
355 |
+
app.querySelector('#txt2img_generate'),
|
356 |
+
Array.from(app.querySelectorAll('#posex-t2i-html')).at(-1), // !
|
357 |
+
{
|
358 |
+
load_all_poses: app.querySelector('#posex-t2i-api-all_pose'),
|
359 |
+
delete_pose: app.querySelector('#posex-t2i-api-delete_pose'),
|
360 |
+
}
|
361 |
+
);
|
362 |
+
}
|
363 |
+
|
364 |
+
async function init_i2i() {
|
365 |
+
const app = gradioApp();
|
366 |
+
await init_canvas(
|
367 |
+
'i2i',
|
368 |
+
app.querySelector('#posex-i2i-enabled input[type=checkbox]'),
|
369 |
+
app.querySelector('#img2img_generate'),
|
370 |
+
Array.from(app.querySelectorAll('#posex-i2i-html')).at(-1), // !
|
371 |
+
{
|
372 |
+
load_all_poses: app.querySelector('#posex-i2i-api-all_pose'),
|
373 |
+
delete_pose: app.querySelector('#posex-i2i-api-delete_pose'),
|
374 |
+
}
|
375 |
+
);
|
376 |
+
}
|
377 |
+
|
378 |
+
if (!globalThis.posex) globalThis.posex = {};
|
379 |
+
const posex = globalThis.posex;
|
380 |
+
posex.init_t2i = init_t2i;
|
381 |
+
posex.init_i2i = init_i2i;
|
382 |
+
|
383 |
+
posex.script_loaded = true;
|
384 |
+
document.dispatchEvent(new CustomEvent('posexscriptloaded'));
|
385 |
+
|
386 |
+
})();
|
posex/javascript/posex-webui.js
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function () {
|
2 |
+
if (!globalThis.posex) globalThis.posex = {};
|
3 |
+
const posex = globalThis.posex;
|
4 |
+
|
5 |
+
function load(cont) {
|
6 |
+
function load_() {
|
7 |
+
if (posex.script_loading || posex.script_loaded) return;
|
8 |
+
posex.script_loading = true;
|
9 |
+
|
10 |
+
const scripts = cont.textContent.trim().split('\n');
|
11 |
+
const base_path = `/file=${scripts.shift()}/js`;
|
12 |
+
cont.textContent = '';
|
13 |
+
|
14 |
+
const df = document.createDocumentFragment();
|
15 |
+
for (let src of scripts) {
|
16 |
+
console.log(`[Posex] loading ${src}`);
|
17 |
+
const script = document.createElement('script');
|
18 |
+
script.async = true;
|
19 |
+
script.type = 'module';
|
20 |
+
script.src = `file=${src}`;
|
21 |
+
df.appendChild(script);
|
22 |
+
}
|
23 |
+
|
24 |
+
globalThis.posex.import = async () => {
|
25 |
+
const THREE = await import(`${base_path}/three.module.js`);
|
26 |
+
const { TrackballControls } = await import(`${base_path}/TrackballControls.js`);
|
27 |
+
const { DragControls } = await import(`${base_path}/DragControls.js`);
|
28 |
+
const { MeshLine, MeshLineMaterial } = await import(`${base_path}/THREE.MeshLine.Module.min.js`);
|
29 |
+
return { THREE, TrackballControls, DragControls, MeshLine, MeshLineMaterial };
|
30 |
+
};
|
31 |
+
if (!globalThis.posex.imports) globalThis.posex.imports = {};
|
32 |
+
if (!globalThis.posex.imports.three) globalThis.posex.imports.three = async () => await import(`${base_path}/three.module.js`);
|
33 |
+
if (!globalThis.posex.imports.posex) globalThis.posex.imports.posex = async () => await import(`${base_path}/posex.js`);
|
34 |
+
cont.appendChild(df);
|
35 |
+
}
|
36 |
+
|
37 |
+
return posex.script_loaded ? Promise.resolve() : new Promise(resolve => {
|
38 |
+
document.addEventListener('posexscriptloaded', () => resolve(), false);
|
39 |
+
load_();
|
40 |
+
});
|
41 |
+
}
|
42 |
+
|
43 |
+
function lazy(fn, timeout) {
|
44 |
+
if (timeout === undefined) timeout = 500;
|
45 |
+
return new Promise(function callback(resolve) {
|
46 |
+
const result = fn();
|
47 |
+
if (result) {
|
48 |
+
resolve(result);
|
49 |
+
} else {
|
50 |
+
setTimeout(() => callback(resolve), timeout);
|
51 |
+
}
|
52 |
+
});
|
53 |
+
}
|
54 |
+
|
55 |
+
function hook_acc(acc, fn) {
|
56 |
+
const observer = new MutationObserver(list => {
|
57 |
+
for (let mut of list) {
|
58 |
+
if (mut.type === 'childList') {
|
59 |
+
if (mut.addedNodes.length != 0) {
|
60 |
+
// closed -> opened
|
61 |
+
fn();
|
62 |
+
} else {
|
63 |
+
// opened -> closed
|
64 |
+
// do nothing
|
65 |
+
}
|
66 |
+
}
|
67 |
+
}
|
68 |
+
});
|
69 |
+
observer.observe(acc, { childList: true, attributes: false, subtree: false });
|
70 |
+
}
|
71 |
+
|
72 |
+
function launch(type) {
|
73 |
+
return lazy(() => gradioApp()?.querySelector(`#posex-${type}-accordion`)).
|
74 |
+
then(acc => hook_acc(acc, async () => {
|
75 |
+
const cont = Array.from(acc.querySelectorAll(`#posex-${type}-js`)).at(-1); // !
|
76 |
+
const enabled = acc.querySelector(`#posex-${type}-enabled input[type=checkbox]`);
|
77 |
+
await load(cont);
|
78 |
+
if (enabled.checked) {
|
79 |
+
await posex[`init_${type}`]();
|
80 |
+
console.log(`[Posex] ${type} initialized`);
|
81 |
+
} else {
|
82 |
+
enabled.addEventListener('change', async () => {
|
83 |
+
await posex[`init_${type}`]();
|
84 |
+
console.log(`[Posex] ${type} initialized`);
|
85 |
+
}, { once: true });
|
86 |
+
}
|
87 |
+
}));
|
88 |
+
}
|
89 |
+
|
90 |
+
launch('t2i');
|
91 |
+
launch('i2i');
|
92 |
+
|
93 |
+
})();
|
posex/js/DragControls.js
ADDED
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
async function _import() {
|
2 |
+
if (!globalThis.posex || !globalThis.posex.import) {
|
3 |
+
return await import('three');
|
4 |
+
} else {
|
5 |
+
return await globalThis.posex.imports.three();
|
6 |
+
}
|
7 |
+
}
|
8 |
+
const {
|
9 |
+
EventDispatcher,
|
10 |
+
Matrix4,
|
11 |
+
Plane,
|
12 |
+
Raycaster,
|
13 |
+
Vector2,
|
14 |
+
Vector3
|
15 |
+
} = await _import();
|
16 |
+
|
17 |
+
const _plane = new Plane();
|
18 |
+
const _raycaster = new Raycaster();
|
19 |
+
|
20 |
+
const _pointer = new Vector2();
|
21 |
+
const _offset = new Vector3();
|
22 |
+
const _intersection = new Vector3();
|
23 |
+
const _worldPosition = new Vector3();
|
24 |
+
const _inverseMatrix = new Matrix4();
|
25 |
+
|
26 |
+
class DragControls extends EventDispatcher {
|
27 |
+
|
28 |
+
constructor( _objects, _camera, _domElement ) {
|
29 |
+
|
30 |
+
super();
|
31 |
+
|
32 |
+
_domElement.style.touchAction = 'none'; // disable touch scroll
|
33 |
+
|
34 |
+
let _selected = null, _hovered = null;
|
35 |
+
|
36 |
+
const _intersections = [];
|
37 |
+
|
38 |
+
//
|
39 |
+
|
40 |
+
const scope = this;
|
41 |
+
|
42 |
+
function activate() {
|
43 |
+
|
44 |
+
_domElement.addEventListener( 'pointermove', onPointerMove );
|
45 |
+
_domElement.addEventListener( 'pointerdown', onPointerDown );
|
46 |
+
_domElement.addEventListener( 'pointerup', onPointerCancel );
|
47 |
+
_domElement.addEventListener( 'pointerleave', onPointerCancel );
|
48 |
+
|
49 |
+
}
|
50 |
+
|
51 |
+
function deactivate() {
|
52 |
+
|
53 |
+
_domElement.removeEventListener( 'pointermove', onPointerMove );
|
54 |
+
_domElement.removeEventListener( 'pointerdown', onPointerDown );
|
55 |
+
_domElement.removeEventListener( 'pointerup', onPointerCancel );
|
56 |
+
_domElement.removeEventListener( 'pointerleave', onPointerCancel );
|
57 |
+
|
58 |
+
_domElement.style.cursor = '';
|
59 |
+
|
60 |
+
}
|
61 |
+
|
62 |
+
function dispose() {
|
63 |
+
|
64 |
+
deactivate();
|
65 |
+
|
66 |
+
}
|
67 |
+
|
68 |
+
function getObjects() {
|
69 |
+
|
70 |
+
return _objects;
|
71 |
+
|
72 |
+
}
|
73 |
+
|
74 |
+
function getRaycaster() {
|
75 |
+
|
76 |
+
return _raycaster;
|
77 |
+
|
78 |
+
}
|
79 |
+
|
80 |
+
function onPointerMove( event ) {
|
81 |
+
|
82 |
+
if ( scope.enabled === false ) return;
|
83 |
+
|
84 |
+
updatePointer( event );
|
85 |
+
|
86 |
+
_raycaster.setFromCamera( _pointer, _camera );
|
87 |
+
|
88 |
+
if ( _selected ) {
|
89 |
+
|
90 |
+
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
|
91 |
+
|
92 |
+
_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
|
93 |
+
|
94 |
+
}
|
95 |
+
|
96 |
+
scope.dispatchEvent( { type: 'drag', object: _selected } );
|
97 |
+
|
98 |
+
return;
|
99 |
+
|
100 |
+
}
|
101 |
+
|
102 |
+
// hover support
|
103 |
+
|
104 |
+
if ( event.pointerType === 'mouse' || event.pointerType === 'pen' ) {
|
105 |
+
|
106 |
+
_intersections.length = 0;
|
107 |
+
|
108 |
+
_raycaster.setFromCamera( _pointer, _camera );
|
109 |
+
_raycaster.intersectObjects( _objects, true, _intersections );
|
110 |
+
|
111 |
+
if ( _intersections.length > 0 ) {
|
112 |
+
|
113 |
+
const object = _intersections[ 0 ].object;
|
114 |
+
|
115 |
+
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
|
116 |
+
|
117 |
+
if ( _hovered !== object && _hovered !== null ) {
|
118 |
+
|
119 |
+
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
|
120 |
+
|
121 |
+
_domElement.style.cursor = 'auto';
|
122 |
+
_hovered = null;
|
123 |
+
|
124 |
+
}
|
125 |
+
|
126 |
+
if ( _hovered !== object ) {
|
127 |
+
|
128 |
+
scope.dispatchEvent( { type: 'hoveron', object: object } );
|
129 |
+
|
130 |
+
_domElement.style.cursor = 'pointer';
|
131 |
+
_hovered = object;
|
132 |
+
|
133 |
+
}
|
134 |
+
|
135 |
+
} else {
|
136 |
+
|
137 |
+
if ( _hovered !== null ) {
|
138 |
+
|
139 |
+
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
|
140 |
+
|
141 |
+
_domElement.style.cursor = 'auto';
|
142 |
+
_hovered = null;
|
143 |
+
|
144 |
+
}
|
145 |
+
|
146 |
+
}
|
147 |
+
|
148 |
+
}
|
149 |
+
|
150 |
+
}
|
151 |
+
|
152 |
+
function onPointerDown( event ) {
|
153 |
+
|
154 |
+
if ( scope.enabled === false ) return;
|
155 |
+
|
156 |
+
updatePointer( event );
|
157 |
+
|
158 |
+
_intersections.length = 0;
|
159 |
+
|
160 |
+
_raycaster.setFromCamera( _pointer, _camera );
|
161 |
+
_raycaster.intersectObjects( _objects, true, _intersections );
|
162 |
+
|
163 |
+
if ( _intersections.length > 0 ) {
|
164 |
+
|
165 |
+
_selected = ( scope.transformGroup === true ) ? _objects[ 0 ] : _intersections[ 0 ].object;
|
166 |
+
|
167 |
+
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
|
168 |
+
|
169 |
+
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
|
170 |
+
|
171 |
+
_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
|
172 |
+
_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
|
173 |
+
|
174 |
+
}
|
175 |
+
|
176 |
+
_domElement.style.cursor = 'move';
|
177 |
+
|
178 |
+
scope.dispatchEvent( { type: 'dragstart', object: _selected } );
|
179 |
+
|
180 |
+
}
|
181 |
+
|
182 |
+
|
183 |
+
}
|
184 |
+
|
185 |
+
function onPointerCancel() {
|
186 |
+
|
187 |
+
if ( scope.enabled === false ) return;
|
188 |
+
|
189 |
+
if ( _selected ) {
|
190 |
+
|
191 |
+
scope.dispatchEvent( { type: 'dragend', object: _selected } );
|
192 |
+
|
193 |
+
_selected = null;
|
194 |
+
|
195 |
+
}
|
196 |
+
|
197 |
+
_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
|
198 |
+
|
199 |
+
}
|
200 |
+
|
201 |
+
function updatePointer( event ) {
|
202 |
+
|
203 |
+
const rect = _domElement.getBoundingClientRect();
|
204 |
+
|
205 |
+
_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
|
206 |
+
_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
|
207 |
+
|
208 |
+
}
|
209 |
+
|
210 |
+
activate();
|
211 |
+
|
212 |
+
// API
|
213 |
+
|
214 |
+
this.enabled = true;
|
215 |
+
this.transformGroup = false;
|
216 |
+
|
217 |
+
this.activate = activate;
|
218 |
+
this.deactivate = deactivate;
|
219 |
+
this.dispose = dispose;
|
220 |
+
this.getObjects = getObjects;
|
221 |
+
this.getRaycaster = getRaycaster;
|
222 |
+
this.onPointerDown = onPointerDown;
|
223 |
+
|
224 |
+
}
|
225 |
+
|
226 |
+
}
|
227 |
+
|
228 |
+
export { DragControls };
|
posex/js/LICENSE/THREE.MeshLine
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2016 Jaume Sanchez
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
posex/js/LICENSE/es-module-shims
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
-----------
|
3 |
+
|
4 |
+
Copyright (C) 2018-2021 Guy Bedford
|
5 |
+
|
6 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
7 |
+
|
8 |
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
9 |
+
|
10 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
posex/js/LICENSE/three
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
The MIT License
|
2 |
+
|
3 |
+
Copyright © 2010-2023 three.js authors
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in
|
13 |
+
all copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21 |
+
THE SOFTWARE.
|
posex/js/THREE.MeshLine.Module.min.js
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Minified by jsDelivr using Terser v5.15.1.
|
3 |
+
* Original file: /npm/[email protected]/src/THREE.MeshLine.Module.js
|
4 |
+
*
|
5 |
+
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
6 |
+
*/
|
7 |
+
async function _import() {
|
8 |
+
if (!globalThis.posex || !globalThis.posex.import) {
|
9 |
+
return await import('three');
|
10 |
+
} else {
|
11 |
+
return await globalThis.posex.imports.three();
|
12 |
+
}
|
13 |
+
}
|
14 |
+
const THREE=await _import();class MeshLine extends THREE.BufferGeometry{constructor(){super(),this.isMeshLine=!0,this.type="MeshLine",this.positions=[],this.previous=[],this.next=[],this.side=[],this.width=[],this.indices_array=[],this.uvs=[],this.counters=[],this._points=[],this._geom=null,this.widthCallback=null,this.matrixWorld=new THREE.Matrix4,Object.defineProperties(this,{geometry:{enumerable:!0,get:function(){return this}},geom:{enumerable:!0,get:function(){return this._geom},set:function(t){this.setGeometry(t,this.widthCallback)}},points:{enumerable:!0,get:function(){return this._points},set:function(t){this.setPoints(t,this.widthCallback)}}})}}function MeshLineRaycast(t,e){var i=new THREE.Matrix4,s=new THREE.Ray,r=new THREE.Sphere,a=new THREE.Vector3,n=this.geometry;if(n.boundingSphere||n.computeBoundingSphere(),r.copy(n.boundingSphere),r.applyMatrix4(this.matrixWorld),!1!==t.ray.intersectSphere(r,a)){i.copy(this.matrixWorld).invert(),s.copy(t.ray).applyMatrix4(i);var o=new THREE.Vector3,u=new THREE.Vector3,h=new THREE.Vector3,l=this instanceof THREE.LineSegments?2:1,p=n.index,c=n.attributes;if(null!==p)for(var f=p.array,v=c.position.array,d=c.width.array,y=0,m=f.length-1;y<m;y+=l){var b=f[y],x=f[y+1];o.fromArray(v,3*b),u.fromArray(v,3*x);var E=void 0!==d[Math.floor(y/3)]?d[Math.floor(y/3)]:1,g=t.params.Line.threshold+this.material.lineWidth*E/2,A=g*g;if(!(s.distanceSqToSegment(o,u,a,h)>A)){a.applyMatrix4(this.matrixWorld);var M=t.ray.origin.distanceTo(a);M<t.near||M>t.far||(e.push({distance:M,point:h.clone().applyMatrix4(this.matrixWorld),index:y,face:null,faceIndex:null,object:this}),y=m)}}}}function memcpy(t,e,i,s,r){var a;if(t=t.subarray||t.slice?t:t.buffer,i=i.subarray||i.slice?i:i.buffer,t=e?t.subarray?t.subarray(e,r&&e+r):t.slice(e,r&&e+r):t,i.set)i.set(t,s);else for(a=0;a<t.length;a++)i[a+s]=t[a];return i}MeshLine.prototype.setMatrixWorld=function(t){this.matrixWorld=t},MeshLine.prototype.setGeometry=function(t,e){this._geometry=t,this.setPoints(t.getAttribute("position").array,e)},MeshLine.prototype.setPoints=function(t,e){if(t instanceof Float32Array||t instanceof Array){if(this._points=t,this.widthCallback=e,this.positions=[],this.counters=[],t.length&&t[0]instanceof THREE.Vector3)for(var i=0;i<t.length;i++){var s=t[i],r=i/t.length;this.positions.push(s.x,s.y,s.z),this.positions.push(s.x,s.y,s.z),this.counters.push(r),this.counters.push(r)}else for(i=0;i<t.length;i+=3){r=i/t.length;this.positions.push(t[i],t[i+1],t[i+2]),this.positions.push(t[i],t[i+1],t[i+2]),this.counters.push(r),this.counters.push(r)}this.process()}else console.error("ERROR: The BufferArray of points is not instancied correctly.")},MeshLine.prototype.raycast=MeshLineRaycast,MeshLine.prototype.compareV3=function(t,e){var i=6*t,s=6*e;return this.positions[i]===this.positions[s]&&this.positions[i+1]===this.positions[s+1]&&this.positions[i+2]===this.positions[s+2]},MeshLine.prototype.copyV3=function(t){var e=6*t;return[this.positions[e],this.positions[e+1],this.positions[e+2]]},MeshLine.prototype.process=function(){var t,e,i=this.positions.length/6;this.previous=[],this.next=[],this.side=[],this.width=[],this.indices_array=[],this.uvs=[],e=this.compareV3(0,i-1)?this.copyV3(i-2):this.copyV3(0),this.previous.push(e[0],e[1],e[2]),this.previous.push(e[0],e[1],e[2]);for(var s=0;s<i;s++){if(this.side.push(1),this.side.push(-1),t=this.widthCallback?this.widthCallback(s/(i-1)):1,this.width.push(t),this.width.push(t),this.uvs.push(s/(i-1),0),this.uvs.push(s/(i-1),1),s<i-1){e=this.copyV3(s),this.previous.push(e[0],e[1],e[2]),this.previous.push(e[0],e[1],e[2]);var r=2*s;this.indices_array.push(r,r+1,r+2),this.indices_array.push(r+2,r+1,r+3)}s>0&&(e=this.copyV3(s),this.next.push(e[0],e[1],e[2]),this.next.push(e[0],e[1],e[2]))}e=this.compareV3(i-1,0)?this.copyV3(1):this.copyV3(i-1),this.next.push(e[0],e[1],e[2]),this.next.push(e[0],e[1],e[2]),this._attributes&&this._attributes.position.count===this.positions.length?(this._attributes.position.copyArray(new Float32Array(this.positions)),this._attributes.position.needsUpdate=!0,this._attributes.previous.copyArray(new Float32Array(this.previous)),this._attributes.previous.needsUpdate=!0,this._attributes.next.copyArray(new Float32Array(this.next)),this._attributes.next.needsUpdate=!0,this._attributes.side.copyArray(new Float32Array(this.side)),this._attributes.side.needsUpdate=!0,this._attributes.width.copyArray(new Float32Array(this.width)),this._attributes.width.needsUpdate=!0,this._attributes.uv.copyArray(new Float32Array(this.uvs)),this._attributes.uv.needsUpdate=!0,this._attributes.index.copyArray(new Uint16Array(this.indices_array)),this._attributes.index.needsUpdate=!0):this._attributes={position:new THREE.BufferAttribute(new Float32Array(this.positions),3),previous:new THREE.BufferAttribute(new Float32Array(this.previous),3),next:new THREE.BufferAttribute(new Float32Array(this.next),3),side:new THREE.BufferAttribute(new Float32Array(this.side),1),width:new THREE.BufferAttribute(new Float32Array(this.width),1),uv:new THREE.BufferAttribute(new Float32Array(this.uvs),2),index:new THREE.BufferAttribute(new Uint16Array(this.indices_array),1),counters:new THREE.BufferAttribute(new Float32Array(this.counters),1)},this.setAttribute("position",this._attributes.position),this.setAttribute("previous",this._attributes.previous),this.setAttribute("next",this._attributes.next),this.setAttribute("side",this._attributes.side),this.setAttribute("width",this._attributes.width),this.setAttribute("uv",this._attributes.uv),this.setAttribute("counters",this._attributes.counters),this.setIndex(this._attributes.index),this.computeBoundingSphere(),this.computeBoundingBox()},MeshLine.prototype.advance=function(t){var e=this._attributes.position.array,i=this._attributes.previous.array,s=this._attributes.next.array,r=e.length;memcpy(e,0,i,0,r),memcpy(e,6,e,0,r-6),e[r-6]=t.x,e[r-5]=t.y,e[r-4]=t.z,e[r-3]=t.x,e[r-2]=t.y,e[r-1]=t.z,memcpy(e,6,s,0,r-6),s[r-6]=t.x,s[r-5]=t.y,s[r-4]=t.z,s[r-3]=t.x,s[r-2]=t.y,s[r-1]=t.z,this._attributes.position.needsUpdate=!0,this._attributes.previous.needsUpdate=!0,this._attributes.next.needsUpdate=!0},THREE.ShaderChunk.meshline_vert=["",THREE.ShaderChunk.logdepthbuf_pars_vertex,THREE.ShaderChunk.fog_pars_vertex,"","attribute vec3 previous;","attribute vec3 next;","attribute float side;","attribute float width;","attribute float counters;","","uniform vec2 resolution;","uniform float lineWidth;","uniform vec3 color;","uniform float opacity;","uniform float sizeAttenuation;","","varying vec2 vUV;","varying vec4 vColor;","varying float vCounters;","","vec2 fix( vec4 i, float aspect ) {",""," vec2 res = i.xy / i.w;"," res.x *= aspect;","\t vCounters = counters;"," return res;","","}","","void main() {",""," float aspect = resolution.x / resolution.y;",""," vColor = vec4( color, opacity );"," vUV = uv;",""," mat4 m = projectionMatrix * modelViewMatrix;"," vec4 finalPosition = m * vec4( position, 1.0 );"," vec4 prevPos = m * vec4( previous, 1.0 );"," vec4 nextPos = m * vec4( next, 1.0 );",""," vec2 currentP = fix( finalPosition, aspect );"," vec2 prevP = fix( prevPos, aspect );"," vec2 nextP = fix( nextPos, aspect );",""," float w = lineWidth * width;",""," vec2 dir;"," if( nextP == currentP ) dir = normalize( currentP - prevP );"," else if( prevP == currentP ) dir = normalize( nextP - currentP );"," else {"," vec2 dir1 = normalize( currentP - prevP );"," vec2 dir2 = normalize( nextP - currentP );"," dir = normalize( dir1 + dir2 );",""," vec2 perp = vec2( -dir1.y, dir1.x );"," vec2 miter = vec2( -dir.y, dir.x );"," //w = clamp( w / dot( miter, perp ), 0., 4. * lineWidth * width );",""," }",""," //vec2 normal = ( cross( vec3( dir, 0. ), vec3( 0., 0., 1. ) ) ).xy;"," vec4 normal = vec4( -dir.y, dir.x, 0., 1. );"," normal.xy *= .5 * w;"," normal *= projectionMatrix;"," if( sizeAttenuation == 0. ) {"," normal.xy *= finalPosition.w;"," normal.xy /= ( vec4( resolution, 0., 1. ) * projectionMatrix ).xy;"," }",""," finalPosition.xy += normal.xy * side;",""," gl_Position = finalPosition;","",THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.fog_vertex&&" vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",THREE.ShaderChunk.fog_vertex,"}"].join("\n"),THREE.ShaderChunk.meshline_frag=["",THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"","uniform sampler2D map;","uniform sampler2D alphaMap;","uniform float useMap;","uniform float useAlphaMap;","uniform float useDash;","uniform float dashArray;","uniform float dashOffset;","uniform float dashRatio;","uniform float visibility;","uniform float alphaTest;","uniform vec2 repeat;","","varying vec2 vUV;","varying vec4 vColor;","varying float vCounters;","","void main() {","",THREE.ShaderChunk.logdepthbuf_fragment,""," vec4 c = vColor;"," if( useMap == 1. ) c *= texture2D( map, vUV * repeat );"," if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUV * repeat ).a;"," if( c.a < alphaTest ) discard;"," if( useDash == 1. ){"," c.a *= ceil(mod(vCounters + dashOffset, dashArray) - (dashArray * dashRatio));"," }"," gl_FragColor = c;"," gl_FragColor.a *= step(vCounters, visibility);","",THREE.ShaderChunk.fog_fragment,"}"].join("\n");class MeshLineMaterial extends THREE.ShaderMaterial{constructor(t){super({uniforms:Object.assign({},THREE.UniformsLib.fog,{lineWidth:{value:1},map:{value:null},useMap:{value:0},alphaMap:{value:null},useAlphaMap:{value:0},color:{value:new THREE.Color(16777215)},opacity:{value:1},resolution:{value:new THREE.Vector2(1,1)},sizeAttenuation:{value:1},dashArray:{value:0},dashOffset:{value:0},dashRatio:{value:.5},useDash:{value:0},visibility:{value:1},alphaTest:{value:0},repeat:{value:new THREE.Vector2(1,1)}}),vertexShader:THREE.ShaderChunk.meshline_vert,fragmentShader:THREE.ShaderChunk.meshline_frag}),this.isMeshLineMaterial=!0,this.type="MeshLineMaterial",Object.defineProperties(this,{lineWidth:{enumerable:!0,get:function(){return this.uniforms.lineWidth.value},set:function(t){this.uniforms.lineWidth.value=t}},map:{enumerable:!0,get:function(){return this.uniforms.map.value},set:function(t){this.uniforms.map.value=t}},useMap:{enumerable:!0,get:function(){return this.uniforms.useMap.value},set:function(t){this.uniforms.useMap.value=t}},alphaMap:{enumerable:!0,get:function(){return this.uniforms.alphaMap.value},set:function(t){this.uniforms.alphaMap.value=t}},useAlphaMap:{enumerable:!0,get:function(){return this.uniforms.useAlphaMap.value},set:function(t){this.uniforms.useAlphaMap.value=t}},color:{enumerable:!0,get:function(){return this.uniforms.color.value},set:function(t){this.uniforms.color.value=t}},opacity:{enumerable:!0,get:function(){return this.uniforms.opacity.value},set:function(t){this.uniforms.opacity.value=t}},resolution:{enumerable:!0,get:function(){return this.uniforms.resolution.value},set:function(t){this.uniforms.resolution.value.copy(t)}},sizeAttenuation:{enumerable:!0,get:function(){return this.uniforms.sizeAttenuation.value},set:function(t){this.uniforms.sizeAttenuation.value=t}},dashArray:{enumerable:!0,get:function(){return this.uniforms.dashArray.value},set:function(t){this.uniforms.dashArray.value=t,this.useDash=0!==t?1:0}},dashOffset:{enumerable:!0,get:function(){return this.uniforms.dashOffset.value},set:function(t){this.uniforms.dashOffset.value=t}},dashRatio:{enumerable:!0,get:function(){return this.uniforms.dashRatio.value},set:function(t){this.uniforms.dashRatio.value=t}},useDash:{enumerable:!0,get:function(){return this.uniforms.useDash.value},set:function(t){this.uniforms.useDash.value=t}},visibility:{enumerable:!0,get:function(){return this.uniforms.visibility.value},set:function(t){this.uniforms.visibility.value=t}},alphaTest:{enumerable:!0,get:function(){return this.uniforms.alphaTest.value},set:function(t){this.uniforms.alphaTest.value=t}},repeat:{enumerable:!0,get:function(){return this.uniforms.repeat.value},set:function(t){this.uniforms.repeat.value.copy(t)}}}),this.setValues(t)}}MeshLineMaterial.prototype.copy=function(t){return THREE.ShaderMaterial.prototype.copy.call(this,t),this.lineWidth=t.lineWidth,this.map=t.map,this.useMap=t.useMap,this.alphaMap=t.alphaMap,this.useAlphaMap=t.useAlphaMap,this.color.copy(t.color),this.opacity=t.opacity,this.resolution.copy(t.resolution),this.sizeAttenuation=t.sizeAttenuation,this.dashArray.copy(t.dashArray),this.dashOffset.copy(t.dashOffset),this.dashRatio.copy(t.dashRatio),this.useDash=t.useDash,this.visibility=t.visibility,this.alphaTest=t.alphaTest,this.repeat.copy(t.repeat),this};export{MeshLine,MeshLineMaterial,MeshLineRaycast};
|
15 |
+
//# sourceMappingURL=/sm/a10f0fbff9ef9e01c8548236b6b20610cf5d5cba1f631d8ebab539add9c04974.map
|
posex/js/TrackballControls.js
ADDED
@@ -0,0 +1,821 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
async function _import() {
|
2 |
+
if (!globalThis.posex || !globalThis.posex.import) {
|
3 |
+
return await import('three');
|
4 |
+
} else {
|
5 |
+
return await globalThis.posex.imports.three();
|
6 |
+
}
|
7 |
+
}
|
8 |
+
const {
|
9 |
+
EventDispatcher,
|
10 |
+
MOUSE,
|
11 |
+
Quaternion,
|
12 |
+
Vector2,
|
13 |
+
Vector3
|
14 |
+
} = await _import();
|
15 |
+
|
16 |
+
const _changeEvent = { type: 'change' };
|
17 |
+
const _startEvent = { type: 'start' };
|
18 |
+
const _endEvent = { type: 'end' };
|
19 |
+
|
20 |
+
class TrackballControls extends EventDispatcher {
|
21 |
+
|
22 |
+
constructor( object, domElement ) {
|
23 |
+
|
24 |
+
super();
|
25 |
+
|
26 |
+
const scope = this;
|
27 |
+
const STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
|
28 |
+
|
29 |
+
this.object = object;
|
30 |
+
this.domElement = domElement;
|
31 |
+
this.domElement.style.touchAction = 'none'; // disable touch scroll
|
32 |
+
|
33 |
+
// API
|
34 |
+
|
35 |
+
this.enabled = true;
|
36 |
+
|
37 |
+
this.screen = { left: 0, top: 0, width: 0, height: 0 };
|
38 |
+
|
39 |
+
this.rotateSpeed = 1.0;
|
40 |
+
this.zoomSpeed = 1.2;
|
41 |
+
this.panSpeed = 0.3;
|
42 |
+
|
43 |
+
this.noRotate = false;
|
44 |
+
this.noZoom = false;
|
45 |
+
this.noPan = false;
|
46 |
+
|
47 |
+
this.staticMoving = false;
|
48 |
+
this.dynamicDampingFactor = 0.2;
|
49 |
+
|
50 |
+
this.minDistance = 0;
|
51 |
+
this.maxDistance = Infinity;
|
52 |
+
|
53 |
+
this.keys = [ 'KeyA' /*A*/, 'KeyS' /*S*/, 'KeyD' /*D*/ ];
|
54 |
+
|
55 |
+
this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
|
56 |
+
|
57 |
+
// internals
|
58 |
+
|
59 |
+
this.target = new Vector3();
|
60 |
+
|
61 |
+
const EPS = 0.000001;
|
62 |
+
|
63 |
+
const lastPosition = new Vector3();
|
64 |
+
let lastZoom = 1;
|
65 |
+
|
66 |
+
let _state = STATE.NONE,
|
67 |
+
_keyState = STATE.NONE,
|
68 |
+
|
69 |
+
_touchZoomDistanceStart = 0,
|
70 |
+
_touchZoomDistanceEnd = 0,
|
71 |
+
|
72 |
+
_lastAngle = 0;
|
73 |
+
|
74 |
+
const _eye = new Vector3(),
|
75 |
+
|
76 |
+
_movePrev = new Vector2(),
|
77 |
+
_moveCurr = new Vector2(),
|
78 |
+
|
79 |
+
_lastAxis = new Vector3(),
|
80 |
+
|
81 |
+
_zoomStart = new Vector2(),
|
82 |
+
_zoomEnd = new Vector2(),
|
83 |
+
|
84 |
+
_panStart = new Vector2(),
|
85 |
+
_panEnd = new Vector2(),
|
86 |
+
|
87 |
+
_pointers = [],
|
88 |
+
_pointerPositions = {};
|
89 |
+
|
90 |
+
// for reset
|
91 |
+
|
92 |
+
this.target0 = this.target.clone();
|
93 |
+
this.position0 = this.object.position.clone();
|
94 |
+
this.up0 = this.object.up.clone();
|
95 |
+
this.zoom0 = this.object.zoom;
|
96 |
+
|
97 |
+
// methods
|
98 |
+
|
99 |
+
this.handleResize = function () {
|
100 |
+
|
101 |
+
const box = scope.domElement.getBoundingClientRect();
|
102 |
+
// adjustments come from similar code in the jquery offset() function
|
103 |
+
const d = scope.domElement.ownerDocument.documentElement;
|
104 |
+
scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
|
105 |
+
scope.screen.top = box.top + window.pageYOffset - d.clientTop;
|
106 |
+
scope.screen.width = box.width;
|
107 |
+
scope.screen.height = box.height;
|
108 |
+
|
109 |
+
};
|
110 |
+
|
111 |
+
const getMouseOnScreen = ( function () {
|
112 |
+
|
113 |
+
const vector = new Vector2();
|
114 |
+
|
115 |
+
return function getMouseOnScreen( pageX, pageY ) {
|
116 |
+
|
117 |
+
vector.set(
|
118 |
+
( pageX - scope.screen.left ) / scope.screen.width,
|
119 |
+
( pageY - scope.screen.top ) / scope.screen.height
|
120 |
+
);
|
121 |
+
|
122 |
+
return vector;
|
123 |
+
|
124 |
+
};
|
125 |
+
|
126 |
+
}() );
|
127 |
+
|
128 |
+
const getMouseOnCircle = ( function () {
|
129 |
+
|
130 |
+
const vector = new Vector2();
|
131 |
+
|
132 |
+
return function getMouseOnCircle( pageX, pageY ) {
|
133 |
+
|
134 |
+
vector.set(
|
135 |
+
( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ) ),
|
136 |
+
( ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width ) // screen.width intentional
|
137 |
+
);
|
138 |
+
|
139 |
+
return vector;
|
140 |
+
|
141 |
+
};
|
142 |
+
|
143 |
+
}() );
|
144 |
+
|
145 |
+
this.rotateCamera = ( function () {
|
146 |
+
|
147 |
+
const axis = new Vector3(),
|
148 |
+
quaternion = new Quaternion(),
|
149 |
+
eyeDirection = new Vector3(),
|
150 |
+
objectUpDirection = new Vector3(),
|
151 |
+
objectSidewaysDirection = new Vector3(),
|
152 |
+
moveDirection = new Vector3();
|
153 |
+
|
154 |
+
return function rotateCamera() {
|
155 |
+
|
156 |
+
moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
|
157 |
+
let angle = moveDirection.length();
|
158 |
+
|
159 |
+
if ( angle ) {
|
160 |
+
|
161 |
+
_eye.copy( scope.object.position ).sub( scope.target );
|
162 |
+
|
163 |
+
eyeDirection.copy( _eye ).normalize();
|
164 |
+
objectUpDirection.copy( scope.object.up ).normalize();
|
165 |
+
objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
|
166 |
+
|
167 |
+
objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
|
168 |
+
objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
|
169 |
+
|
170 |
+
moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
|
171 |
+
|
172 |
+
axis.crossVectors( moveDirection, _eye ).normalize();
|
173 |
+
|
174 |
+
angle *= scope.rotateSpeed;
|
175 |
+
quaternion.setFromAxisAngle( axis, angle );
|
176 |
+
|
177 |
+
_eye.applyQuaternion( quaternion );
|
178 |
+
scope.object.up.applyQuaternion( quaternion );
|
179 |
+
|
180 |
+
_lastAxis.copy( axis );
|
181 |
+
_lastAngle = angle;
|
182 |
+
|
183 |
+
} else if ( ! scope.staticMoving && _lastAngle ) {
|
184 |
+
|
185 |
+
_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
|
186 |
+
_eye.copy( scope.object.position ).sub( scope.target );
|
187 |
+
quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
|
188 |
+
_eye.applyQuaternion( quaternion );
|
189 |
+
scope.object.up.applyQuaternion( quaternion );
|
190 |
+
|
191 |
+
}
|
192 |
+
|
193 |
+
_movePrev.copy( _moveCurr );
|
194 |
+
|
195 |
+
};
|
196 |
+
|
197 |
+
}() );
|
198 |
+
|
199 |
+
|
200 |
+
this.zoomCamera = function () {
|
201 |
+
|
202 |
+
let factor;
|
203 |
+
|
204 |
+
if ( _state === STATE.TOUCH_ZOOM_PAN ) {
|
205 |
+
|
206 |
+
factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
|
207 |
+
_touchZoomDistanceStart = _touchZoomDistanceEnd;
|
208 |
+
|
209 |
+
if ( scope.object.isPerspectiveCamera ) {
|
210 |
+
|
211 |
+
_eye.multiplyScalar( factor );
|
212 |
+
|
213 |
+
} else if ( scope.object.isOrthographicCamera ) {
|
214 |
+
|
215 |
+
scope.object.zoom /= factor;
|
216 |
+
scope.object.updateProjectionMatrix();
|
217 |
+
|
218 |
+
} else {
|
219 |
+
|
220 |
+
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
221 |
+
|
222 |
+
}
|
223 |
+
|
224 |
+
} else {
|
225 |
+
|
226 |
+
factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
|
227 |
+
|
228 |
+
if ( factor !== 1.0 && factor > 0.0 ) {
|
229 |
+
|
230 |
+
if ( scope.object.isPerspectiveCamera ) {
|
231 |
+
|
232 |
+
_eye.multiplyScalar( factor );
|
233 |
+
|
234 |
+
} else if ( scope.object.isOrthographicCamera ) {
|
235 |
+
|
236 |
+
scope.object.zoom /= factor;
|
237 |
+
scope.object.updateProjectionMatrix();
|
238 |
+
|
239 |
+
} else {
|
240 |
+
|
241 |
+
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
242 |
+
|
243 |
+
}
|
244 |
+
|
245 |
+
}
|
246 |
+
|
247 |
+
if ( scope.staticMoving ) {
|
248 |
+
|
249 |
+
_zoomStart.copy( _zoomEnd );
|
250 |
+
|
251 |
+
} else {
|
252 |
+
|
253 |
+
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
|
254 |
+
|
255 |
+
}
|
256 |
+
|
257 |
+
}
|
258 |
+
|
259 |
+
};
|
260 |
+
|
261 |
+
this.panCamera = ( function () {
|
262 |
+
|
263 |
+
const mouseChange = new Vector2(),
|
264 |
+
objectUp = new Vector3(),
|
265 |
+
pan = new Vector3();
|
266 |
+
|
267 |
+
return function panCamera() {
|
268 |
+
|
269 |
+
mouseChange.copy( _panEnd ).sub( _panStart );
|
270 |
+
|
271 |
+
if ( mouseChange.lengthSq() ) {
|
272 |
+
|
273 |
+
if ( scope.object.isOrthographicCamera ) {
|
274 |
+
|
275 |
+
const scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
|
276 |
+
const scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
|
277 |
+
|
278 |
+
mouseChange.x *= scale_x;
|
279 |
+
mouseChange.y *= scale_y;
|
280 |
+
|
281 |
+
}
|
282 |
+
|
283 |
+
mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
|
284 |
+
|
285 |
+
pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
|
286 |
+
pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
|
287 |
+
|
288 |
+
scope.object.position.add( pan );
|
289 |
+
scope.target.add( pan );
|
290 |
+
|
291 |
+
if ( scope.staticMoving ) {
|
292 |
+
|
293 |
+
_panStart.copy( _panEnd );
|
294 |
+
|
295 |
+
} else {
|
296 |
+
|
297 |
+
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
|
298 |
+
|
299 |
+
}
|
300 |
+
|
301 |
+
}
|
302 |
+
|
303 |
+
};
|
304 |
+
|
305 |
+
}() );
|
306 |
+
|
307 |
+
this.checkDistances = function () {
|
308 |
+
|
309 |
+
if ( ! scope.noZoom || ! scope.noPan ) {
|
310 |
+
|
311 |
+
if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
|
312 |
+
|
313 |
+
scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
|
314 |
+
_zoomStart.copy( _zoomEnd );
|
315 |
+
|
316 |
+
}
|
317 |
+
|
318 |
+
if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
|
319 |
+
|
320 |
+
scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
|
321 |
+
_zoomStart.copy( _zoomEnd );
|
322 |
+
|
323 |
+
}
|
324 |
+
|
325 |
+
}
|
326 |
+
|
327 |
+
};
|
328 |
+
|
329 |
+
this.update = function () {
|
330 |
+
|
331 |
+
_eye.subVectors( scope.object.position, scope.target );
|
332 |
+
|
333 |
+
if ( ! scope.noRotate ) {
|
334 |
+
|
335 |
+
scope.rotateCamera();
|
336 |
+
|
337 |
+
}
|
338 |
+
|
339 |
+
if ( ! scope.noZoom ) {
|
340 |
+
|
341 |
+
scope.zoomCamera();
|
342 |
+
|
343 |
+
}
|
344 |
+
|
345 |
+
if ( ! scope.noPan ) {
|
346 |
+
|
347 |
+
scope.panCamera();
|
348 |
+
|
349 |
+
}
|
350 |
+
|
351 |
+
scope.object.position.addVectors( scope.target, _eye );
|
352 |
+
|
353 |
+
if ( scope.object.isPerspectiveCamera ) {
|
354 |
+
|
355 |
+
scope.checkDistances();
|
356 |
+
|
357 |
+
scope.object.lookAt( scope.target );
|
358 |
+
|
359 |
+
if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
|
360 |
+
|
361 |
+
scope.dispatchEvent( _changeEvent );
|
362 |
+
|
363 |
+
lastPosition.copy( scope.object.position );
|
364 |
+
|
365 |
+
}
|
366 |
+
|
367 |
+
} else if ( scope.object.isOrthographicCamera ) {
|
368 |
+
|
369 |
+
scope.object.lookAt( scope.target );
|
370 |
+
|
371 |
+
if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
|
372 |
+
|
373 |
+
scope.dispatchEvent( _changeEvent );
|
374 |
+
|
375 |
+
lastPosition.copy( scope.object.position );
|
376 |
+
lastZoom = scope.object.zoom;
|
377 |
+
|
378 |
+
}
|
379 |
+
|
380 |
+
} else {
|
381 |
+
|
382 |
+
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
383 |
+
|
384 |
+
}
|
385 |
+
|
386 |
+
};
|
387 |
+
|
388 |
+
this.reset = function () {
|
389 |
+
|
390 |
+
_state = STATE.NONE;
|
391 |
+
_keyState = STATE.NONE;
|
392 |
+
|
393 |
+
scope.target.copy( scope.target0 );
|
394 |
+
scope.object.position.copy( scope.position0 );
|
395 |
+
scope.object.up.copy( scope.up0 );
|
396 |
+
scope.object.zoom = scope.zoom0;
|
397 |
+
|
398 |
+
scope.object.updateProjectionMatrix();
|
399 |
+
|
400 |
+
_eye.subVectors( scope.object.position, scope.target );
|
401 |
+
|
402 |
+
scope.object.lookAt( scope.target );
|
403 |
+
|
404 |
+
scope.dispatchEvent( _changeEvent );
|
405 |
+
|
406 |
+
lastPosition.copy( scope.object.position );
|
407 |
+
lastZoom = scope.object.zoom;
|
408 |
+
|
409 |
+
};
|
410 |
+
|
411 |
+
// listeners
|
412 |
+
|
413 |
+
function onPointerDown( event ) {
|
414 |
+
|
415 |
+
if ( scope.enabled === false ) return;
|
416 |
+
|
417 |
+
if ( _pointers.length === 0 ) {
|
418 |
+
|
419 |
+
scope.domElement.setPointerCapture( event.pointerId );
|
420 |
+
|
421 |
+
scope.domElement.addEventListener( 'pointermove', onPointerMove );
|
422 |
+
scope.domElement.addEventListener( 'pointerup', onPointerUp );
|
423 |
+
|
424 |
+
}
|
425 |
+
|
426 |
+
//
|
427 |
+
|
428 |
+
addPointer( event );
|
429 |
+
|
430 |
+
if ( event.pointerType === 'touch' ) {
|
431 |
+
|
432 |
+
onTouchStart( event );
|
433 |
+
|
434 |
+
} else {
|
435 |
+
|
436 |
+
onMouseDown( event );
|
437 |
+
|
438 |
+
}
|
439 |
+
|
440 |
+
}
|
441 |
+
|
442 |
+
function onPointerMove( event ) {
|
443 |
+
|
444 |
+
if ( scope.enabled === false ) return;
|
445 |
+
|
446 |
+
if ( event.pointerType === 'touch' ) {
|
447 |
+
|
448 |
+
onTouchMove( event );
|
449 |
+
|
450 |
+
} else {
|
451 |
+
|
452 |
+
onMouseMove( event );
|
453 |
+
|
454 |
+
}
|
455 |
+
|
456 |
+
}
|
457 |
+
|
458 |
+
function onPointerUp( event ) {
|
459 |
+
|
460 |
+
if ( scope.enabled === false ) return;
|
461 |
+
|
462 |
+
if ( event.pointerType === 'touch' ) {
|
463 |
+
|
464 |
+
onTouchEnd( event );
|
465 |
+
|
466 |
+
} else {
|
467 |
+
|
468 |
+
onMouseUp();
|
469 |
+
|
470 |
+
}
|
471 |
+
|
472 |
+
//
|
473 |
+
|
474 |
+
removePointer( event );
|
475 |
+
|
476 |
+
if ( _pointers.length === 0 ) {
|
477 |
+
|
478 |
+
scope.domElement.releasePointerCapture( event.pointerId );
|
479 |
+
|
480 |
+
scope.domElement.removeEventListener( 'pointermove', onPointerMove );
|
481 |
+
scope.domElement.removeEventListener( 'pointerup', onPointerUp );
|
482 |
+
|
483 |
+
}
|
484 |
+
|
485 |
+
|
486 |
+
}
|
487 |
+
|
488 |
+
function onPointerCancel( event ) {
|
489 |
+
|
490 |
+
removePointer( event );
|
491 |
+
|
492 |
+
}
|
493 |
+
|
494 |
+
function keydown( event ) {
|
495 |
+
|
496 |
+
if ( scope.enabled === false ) return;
|
497 |
+
|
498 |
+
window.removeEventListener( 'keydown', keydown );
|
499 |
+
|
500 |
+
if ( _keyState !== STATE.NONE ) {
|
501 |
+
|
502 |
+
return;
|
503 |
+
|
504 |
+
} else if ( event.code === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
|
505 |
+
|
506 |
+
_keyState = STATE.ROTATE;
|
507 |
+
|
508 |
+
} else if ( event.code === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
|
509 |
+
|
510 |
+
_keyState = STATE.ZOOM;
|
511 |
+
|
512 |
+
} else if ( event.code === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
|
513 |
+
|
514 |
+
_keyState = STATE.PAN;
|
515 |
+
|
516 |
+
}
|
517 |
+
|
518 |
+
}
|
519 |
+
|
520 |
+
function keyup() {
|
521 |
+
|
522 |
+
if ( scope.enabled === false ) return;
|
523 |
+
|
524 |
+
_keyState = STATE.NONE;
|
525 |
+
|
526 |
+
window.addEventListener( 'keydown', keydown );
|
527 |
+
|
528 |
+
}
|
529 |
+
|
530 |
+
function onMouseDown( event ) {
|
531 |
+
|
532 |
+
if ( _state === STATE.NONE ) {
|
533 |
+
|
534 |
+
switch ( event.button ) {
|
535 |
+
|
536 |
+
case scope.mouseButtons.LEFT:
|
537 |
+
_state = STATE.ROTATE;
|
538 |
+
break;
|
539 |
+
|
540 |
+
case scope.mouseButtons.MIDDLE:
|
541 |
+
_state = STATE.ZOOM;
|
542 |
+
break;
|
543 |
+
|
544 |
+
case scope.mouseButtons.RIGHT:
|
545 |
+
_state = STATE.PAN;
|
546 |
+
break;
|
547 |
+
|
548 |
+
}
|
549 |
+
|
550 |
+
}
|
551 |
+
|
552 |
+
const state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
|
553 |
+
|
554 |
+
if ( state === STATE.ROTATE && ! scope.noRotate ) {
|
555 |
+
|
556 |
+
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
557 |
+
_movePrev.copy( _moveCurr );
|
558 |
+
|
559 |
+
} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
|
560 |
+
|
561 |
+
_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
562 |
+
_zoomEnd.copy( _zoomStart );
|
563 |
+
|
564 |
+
} else if ( state === STATE.PAN && ! scope.noPan ) {
|
565 |
+
|
566 |
+
_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
567 |
+
_panEnd.copy( _panStart );
|
568 |
+
|
569 |
+
}
|
570 |
+
|
571 |
+
scope.dispatchEvent( _startEvent );
|
572 |
+
|
573 |
+
}
|
574 |
+
|
575 |
+
function onMouseMove( event ) {
|
576 |
+
|
577 |
+
const state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
|
578 |
+
|
579 |
+
if ( state === STATE.ROTATE && ! scope.noRotate ) {
|
580 |
+
|
581 |
+
_movePrev.copy( _moveCurr );
|
582 |
+
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
583 |
+
|
584 |
+
} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
|
585 |
+
|
586 |
+
_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
587 |
+
|
588 |
+
} else if ( state === STATE.PAN && ! scope.noPan ) {
|
589 |
+
|
590 |
+
_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
591 |
+
|
592 |
+
}
|
593 |
+
|
594 |
+
}
|
595 |
+
|
596 |
+
function onMouseUp() {
|
597 |
+
|
598 |
+
_state = STATE.NONE;
|
599 |
+
|
600 |
+
scope.dispatchEvent( _endEvent );
|
601 |
+
|
602 |
+
}
|
603 |
+
|
604 |
+
function onMouseWheel( event ) {
|
605 |
+
|
606 |
+
if ( scope.enabled === false ) return;
|
607 |
+
|
608 |
+
if ( scope.noZoom === true ) return;
|
609 |
+
|
610 |
+
event.preventDefault();
|
611 |
+
|
612 |
+
switch ( event.deltaMode ) {
|
613 |
+
|
614 |
+
case 2:
|
615 |
+
// Zoom in pages
|
616 |
+
_zoomStart.y -= event.deltaY * 0.025;
|
617 |
+
break;
|
618 |
+
|
619 |
+
case 1:
|
620 |
+
// Zoom in lines
|
621 |
+
_zoomStart.y -= event.deltaY * 0.01;
|
622 |
+
break;
|
623 |
+
|
624 |
+
default:
|
625 |
+
// undefined, 0, assume pixels
|
626 |
+
_zoomStart.y -= event.deltaY * 0.00025;
|
627 |
+
break;
|
628 |
+
|
629 |
+
}
|
630 |
+
|
631 |
+
scope.dispatchEvent( _startEvent );
|
632 |
+
scope.dispatchEvent( _endEvent );
|
633 |
+
|
634 |
+
}
|
635 |
+
|
636 |
+
function onTouchStart( event ) {
|
637 |
+
|
638 |
+
trackPointer( event );
|
639 |
+
|
640 |
+
switch ( _pointers.length ) {
|
641 |
+
|
642 |
+
case 1:
|
643 |
+
_state = STATE.TOUCH_ROTATE;
|
644 |
+
_moveCurr.copy( getMouseOnCircle( _pointers[ 0 ].pageX, _pointers[ 0 ].pageY ) );
|
645 |
+
_movePrev.copy( _moveCurr );
|
646 |
+
break;
|
647 |
+
|
648 |
+
default: // 2 or more
|
649 |
+
_state = STATE.TOUCH_ZOOM_PAN;
|
650 |
+
const dx = _pointers[ 0 ].pageX - _pointers[ 1 ].pageX;
|
651 |
+
const dy = _pointers[ 0 ].pageY - _pointers[ 1 ].pageY;
|
652 |
+
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
|
653 |
+
|
654 |
+
const x = ( _pointers[ 0 ].pageX + _pointers[ 1 ].pageX ) / 2;
|
655 |
+
const y = ( _pointers[ 0 ].pageY + _pointers[ 1 ].pageY ) / 2;
|
656 |
+
_panStart.copy( getMouseOnScreen( x, y ) );
|
657 |
+
_panEnd.copy( _panStart );
|
658 |
+
break;
|
659 |
+
|
660 |
+
}
|
661 |
+
|
662 |
+
scope.dispatchEvent( _startEvent );
|
663 |
+
|
664 |
+
}
|
665 |
+
|
666 |
+
function onTouchMove( event ) {
|
667 |
+
|
668 |
+
trackPointer( event );
|
669 |
+
|
670 |
+
switch ( _pointers.length ) {
|
671 |
+
|
672 |
+
case 1:
|
673 |
+
_movePrev.copy( _moveCurr );
|
674 |
+
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
675 |
+
break;
|
676 |
+
|
677 |
+
default: // 2 or more
|
678 |
+
|
679 |
+
const position = getSecondPointerPosition( event );
|
680 |
+
|
681 |
+
const dx = event.pageX - position.x;
|
682 |
+
const dy = event.pageY - position.y;
|
683 |
+
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
|
684 |
+
|
685 |
+
const x = ( event.pageX + position.x ) / 2;
|
686 |
+
const y = ( event.pageY + position.y ) / 2;
|
687 |
+
_panEnd.copy( getMouseOnScreen( x, y ) );
|
688 |
+
break;
|
689 |
+
|
690 |
+
}
|
691 |
+
|
692 |
+
}
|
693 |
+
|
694 |
+
function onTouchEnd( event ) {
|
695 |
+
|
696 |
+
switch ( _pointers.length ) {
|
697 |
+
|
698 |
+
case 0:
|
699 |
+
_state = STATE.NONE;
|
700 |
+
break;
|
701 |
+
|
702 |
+
case 1:
|
703 |
+
_state = STATE.TOUCH_ROTATE;
|
704 |
+
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
705 |
+
_movePrev.copy( _moveCurr );
|
706 |
+
break;
|
707 |
+
|
708 |
+
case 2:
|
709 |
+
_state = STATE.TOUCH_ZOOM_PAN;
|
710 |
+
|
711 |
+
for ( let i = 0; i < _pointers.length; i ++ ) {
|
712 |
+
|
713 |
+
if ( _pointers[ i ].pointerId !== event.pointerId ) {
|
714 |
+
|
715 |
+
const position = _pointerPositions[ _pointers[ i ].pointerId ];
|
716 |
+
_moveCurr.copy( getMouseOnCircle( position.x, position.y ) );
|
717 |
+
_movePrev.copy( _moveCurr );
|
718 |
+
break;
|
719 |
+
|
720 |
+
}
|
721 |
+
|
722 |
+
}
|
723 |
+
|
724 |
+
break;
|
725 |
+
|
726 |
+
}
|
727 |
+
|
728 |
+
scope.dispatchEvent( _endEvent );
|
729 |
+
|
730 |
+
}
|
731 |
+
|
732 |
+
function contextmenu( event ) {
|
733 |
+
|
734 |
+
if ( scope.enabled === false ) return;
|
735 |
+
|
736 |
+
event.preventDefault();
|
737 |
+
|
738 |
+
}
|
739 |
+
|
740 |
+
function addPointer( event ) {
|
741 |
+
|
742 |
+
_pointers.push( event );
|
743 |
+
|
744 |
+
}
|
745 |
+
|
746 |
+
function removePointer( event ) {
|
747 |
+
|
748 |
+
delete _pointerPositions[ event.pointerId ];
|
749 |
+
|
750 |
+
for ( let i = 0; i < _pointers.length; i ++ ) {
|
751 |
+
|
752 |
+
if ( _pointers[ i ].pointerId == event.pointerId ) {
|
753 |
+
|
754 |
+
_pointers.splice( i, 1 );
|
755 |
+
return;
|
756 |
+
|
757 |
+
}
|
758 |
+
|
759 |
+
}
|
760 |
+
|
761 |
+
}
|
762 |
+
|
763 |
+
function trackPointer( event ) {
|
764 |
+
|
765 |
+
let position = _pointerPositions[ event.pointerId ];
|
766 |
+
|
767 |
+
if ( position === undefined ) {
|
768 |
+
|
769 |
+
position = new Vector2();
|
770 |
+
_pointerPositions[ event.pointerId ] = position;
|
771 |
+
|
772 |
+
}
|
773 |
+
|
774 |
+
position.set( event.pageX, event.pageY );
|
775 |
+
|
776 |
+
}
|
777 |
+
|
778 |
+
function getSecondPointerPosition( event ) {
|
779 |
+
|
780 |
+
const pointer = ( event.pointerId === _pointers[ 0 ].pointerId ) ? _pointers[ 1 ] : _pointers[ 0 ];
|
781 |
+
|
782 |
+
return _pointerPositions[ pointer.pointerId ];
|
783 |
+
|
784 |
+
}
|
785 |
+
|
786 |
+
this.dispose = function () {
|
787 |
+
|
788 |
+
scope.domElement.removeEventListener( 'contextmenu', contextmenu );
|
789 |
+
|
790 |
+
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
|
791 |
+
scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
|
792 |
+
scope.domElement.removeEventListener( 'wheel', onMouseWheel );
|
793 |
+
|
794 |
+
scope.domElement.removeEventListener( 'pointermove', onPointerMove );
|
795 |
+
scope.domElement.removeEventListener( 'pointerup', onPointerUp );
|
796 |
+
|
797 |
+
window.removeEventListener( 'keydown', keydown );
|
798 |
+
window.removeEventListener( 'keyup', keyup );
|
799 |
+
|
800 |
+
};
|
801 |
+
|
802 |
+
this.domElement.addEventListener( 'contextmenu', contextmenu );
|
803 |
+
|
804 |
+
this.domElement.addEventListener( 'pointerdown', onPointerDown );
|
805 |
+
this.domElement.addEventListener( 'pointercancel', onPointerCancel );
|
806 |
+
this.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );
|
807 |
+
|
808 |
+
|
809 |
+
window.addEventListener( 'keydown', keydown );
|
810 |
+
window.addEventListener( 'keyup', keyup );
|
811 |
+
|
812 |
+
this.handleResize();
|
813 |
+
|
814 |
+
// force an update at start
|
815 |
+
this.update();
|
816 |
+
|
817 |
+
}
|
818 |
+
|
819 |
+
}
|
820 |
+
|
821 |
+
export { TrackballControls };
|
posex/js/app.js
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const POSES = new Map();
|
2 |
+
|
3 |
+
function notify(str, type) {
|
4 |
+
if (type === undefined) type = 'success';
|
5 |
+
|
6 |
+
switch (type) {
|
7 |
+
case 'success': console.log(str); break;
|
8 |
+
case 'info': console.log(str); break;
|
9 |
+
case 'warn': console.warn(str); break;
|
10 |
+
case 'error': console.error(str); break;
|
11 |
+
}
|
12 |
+
|
13 |
+
const p = document.createElement('p');
|
14 |
+
p.textContent = str;
|
15 |
+
p.classList.add('item', type);
|
16 |
+
const cont = document.querySelector('#notifications');
|
17 |
+
cont.appendChild(p);
|
18 |
+
setTimeout(() => cont.removeChild(p), 3000);
|
19 |
+
}
|
20 |
+
|
21 |
+
async function save_pose(obj) {
|
22 |
+
const res = await fetch('/pose/save', {
|
23 |
+
method: 'POST',
|
24 |
+
headers: { 'Content-Type': 'application/json' },
|
25 |
+
body: JSON.stringify(obj),
|
26 |
+
});
|
27 |
+
const result = await res.json();
|
28 |
+
if (result.ok) reload_poses();
|
29 |
+
return result;
|
30 |
+
}
|
31 |
+
|
32 |
+
async function delete_pose(name) {
|
33 |
+
const res = await fetch('/pose/delete', {
|
34 |
+
method: 'POST',
|
35 |
+
headers: { 'Content-Type': 'application/json' },
|
36 |
+
body: JSON.stringify({ name }),
|
37 |
+
});
|
38 |
+
const result = await res.json();
|
39 |
+
notify(result.result, result.ok ? 'success' : 'error');
|
40 |
+
if (result.ok) reload_poses();
|
41 |
+
return result;
|
42 |
+
}
|
43 |
+
|
44 |
+
async function reload_poses() {
|
45 |
+
POSES.clear();
|
46 |
+
|
47 |
+
const res = await fetch('/pose/all');
|
48 |
+
const cont = document.querySelector('#saved_poses');
|
49 |
+
cont.innerHTML = '';
|
50 |
+
const df = document.createDocumentFragment();
|
51 |
+
for (let data of await res.json()) {
|
52 |
+
POSES.set(data.name, data);
|
53 |
+
|
54 |
+
const fig = document.createElement('figure')
|
55 |
+
const img = document.createElement('img');
|
56 |
+
const cap = document.createElement('figcaption');
|
57 |
+
const clo = document.createElement('div');
|
58 |
+
const clo2 = document.createElement('span');
|
59 |
+
fig.dataset.poseName = data.name;
|
60 |
+
cap.textContent = data.name;
|
61 |
+
clo.textContent = 'x';
|
62 |
+
clo.classList.add('close');
|
63 |
+
clo2.classList.add('close2');
|
64 |
+
clo2.textContent = 'delete';
|
65 |
+
clo.appendChild(clo2);
|
66 |
+
|
67 |
+
img.src = 'data:image/png;base64,' + data.image;
|
68 |
+
img.title = data.name;
|
69 |
+
fig.append(clo, img, cap);
|
70 |
+
|
71 |
+
df.appendChild(fig);
|
72 |
+
}
|
73 |
+
cont.appendChild(df);
|
74 |
+
}
|
75 |
+
|
76 |
+
document.addEventListener('DOMContentLoaded', async () => {
|
77 |
+
|
78 |
+
const ui = {
|
79 |
+
container: document.querySelector('#cont'),
|
80 |
+
canvas: document.querySelector('#main_canvas'),
|
81 |
+
notation: document.querySelector('#notation'),
|
82 |
+
indicator1: document.querySelector('#body_indicator1'),
|
83 |
+
indicator2: document.querySelector('#body_indicator2'),
|
84 |
+
all_reset: document.querySelector('#all_reset'),
|
85 |
+
reset_camera: document.querySelector('#reset_camera'),
|
86 |
+
reset_pose: document.querySelector('#reset_pose'),
|
87 |
+
fixed_roll: document.querySelector('#fixed_roll'),
|
88 |
+
add_body: document.querySelector('#add_body'),
|
89 |
+
remove_body: document.querySelector('#remove_body'),
|
90 |
+
canvas_width: document.querySelector('#canvas_width'),
|
91 |
+
canvas_height: document.querySelector('#canvas_height'),
|
92 |
+
bg: document.querySelector('#bg_file'),
|
93 |
+
reset_bg: document.querySelector('#reset_bg'),
|
94 |
+
elliptic_limbs: document.querySelector('#elliptic_limbs'),
|
95 |
+
//joint_radius: document.querySelector('#joint_radius'),
|
96 |
+
limb_width: document.querySelector('#limb_width'),
|
97 |
+
low_fps: document.querySelector('#low_fps'),
|
98 |
+
save: document.querySelector('#save_button'),
|
99 |
+
copy: document.querySelector('#copy_button'),
|
100 |
+
save_pose: document.querySelector('#save_pose'),
|
101 |
+
save_pose_callback: save_pose,
|
102 |
+
notify: notify,
|
103 |
+
};
|
104 |
+
|
105 |
+
document.addEventListener('poseload', e => {
|
106 |
+
const obj = POSES.get(e.detail.name);
|
107 |
+
if (obj) ui.loadPose(obj);
|
108 |
+
}, false);
|
109 |
+
|
110 |
+
const { init, init_3d } = await import('posex');
|
111 |
+
|
112 |
+
init(ui);
|
113 |
+
const animate = init_3d(ui);
|
114 |
+
animate();
|
115 |
+
|
116 |
+
await reload_poses();
|
117 |
+
|
118 |
+
}, false);
|
119 |
+
|
120 |
+
document.addEventListener('DOMContentLoaded', () => {
|
121 |
+
const get_name = ele => {
|
122 |
+
while (ele && ele !== document) {
|
123 |
+
if (ele.dataset && ele.dataset.poseName !== undefined)
|
124 |
+
return ele.dataset.poseName;
|
125 |
+
ele = ele.parentNode;
|
126 |
+
}
|
127 |
+
return '';
|
128 |
+
};
|
129 |
+
|
130 |
+
document.querySelector('#saved_poses').addEventListener('click', e => {
|
131 |
+
let target = e.target;
|
132 |
+
if (target.tagName === 'IMG') target = target.parentNode;
|
133 |
+
if (target.classList.contains('close2')) target = target.parentNode;
|
134 |
+
if (target.tagName === 'FIGURE') {
|
135 |
+
const name = get_name(target);
|
136 |
+
const ev = new CustomEvent('poseload', { bubbles: true, detail: { name } });
|
137 |
+
target.dispatchEvent(ev);
|
138 |
+
} else if (target.classList.contains('close')) {
|
139 |
+
const name = get_name(target);
|
140 |
+
if (name.length != 0) {
|
141 |
+
delete_pose(name);
|
142 |
+
}
|
143 |
+
}
|
144 |
+
}, false);
|
145 |
+
}, false);
|
posex/js/es-module-shims.js
ADDED
@@ -0,0 +1,790 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// https://github.com/guybedford/es-module-shims
|
2 |
+
|
3 |
+
/* ES Module Shims 1.3.6 */
|
4 |
+
(function () {
|
5 |
+
|
6 |
+
const edge = navigator.userAgent.match(/Edge\/\d\d\.\d+$/);
|
7 |
+
|
8 |
+
let baseUrl;
|
9 |
+
|
10 |
+
function createBlob(source, type = 'text/javascript') {
|
11 |
+
return URL.createObjectURL(new Blob([source], { type }));
|
12 |
+
}
|
13 |
+
|
14 |
+
const noop = () => { };
|
15 |
+
|
16 |
+
const baseEl = document.querySelector('base[href]');
|
17 |
+
if (baseEl)
|
18 |
+
baseUrl = baseEl.href;
|
19 |
+
|
20 |
+
if (!baseUrl && typeof location !== 'undefined') {
|
21 |
+
baseUrl = location.href.split('#')[0].split('?')[0];
|
22 |
+
const lastSepIndex = baseUrl.lastIndexOf('/');
|
23 |
+
if (lastSepIndex !== -1)
|
24 |
+
baseUrl = baseUrl.slice(0, lastSepIndex + 1);
|
25 |
+
}
|
26 |
+
|
27 |
+
function isURL(url) {
|
28 |
+
try {
|
29 |
+
new URL(url);
|
30 |
+
return true;
|
31 |
+
}
|
32 |
+
catch {
|
33 |
+
return false;
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
const backslashRegEx = /\\/g;
|
38 |
+
function resolveIfNotPlainOrUrl(relUrl, parentUrl) {
|
39 |
+
// strip off any trailing query params or hashes
|
40 |
+
parentUrl = parentUrl && parentUrl.split('#')[0].split('?')[0];
|
41 |
+
if (relUrl.indexOf('\\') !== -1)
|
42 |
+
relUrl = relUrl.replace(backslashRegEx, '/');
|
43 |
+
// protocol-relative
|
44 |
+
if (relUrl[0] === '/' && relUrl[1] === '/') {
|
45 |
+
return parentUrl.slice(0, parentUrl.indexOf(':') + 1) + relUrl;
|
46 |
+
}
|
47 |
+
// relative-url
|
48 |
+
else if (relUrl[0] === '.' && (relUrl[1] === '/' || relUrl[1] === '.' && (relUrl[2] === '/' || relUrl.length === 2 && (relUrl += '/')) ||
|
49 |
+
relUrl.length === 1 && (relUrl += '/')) ||
|
50 |
+
relUrl[0] === '/') {
|
51 |
+
const parentProtocol = parentUrl.slice(0, parentUrl.indexOf(':') + 1);
|
52 |
+
// Disabled, but these cases will give inconsistent results for deep backtracking
|
53 |
+
//if (parentUrl[parentProtocol.length] !== '/')
|
54 |
+
// throw new Error('Cannot resolve');
|
55 |
+
// read pathname from parent URL
|
56 |
+
// pathname taken to be part after leading "/"
|
57 |
+
let pathname;
|
58 |
+
if (parentUrl[parentProtocol.length + 1] === '/') {
|
59 |
+
// resolving to a :// so we need to read out the auth and host
|
60 |
+
if (parentProtocol !== 'file:') {
|
61 |
+
pathname = parentUrl.slice(parentProtocol.length + 2);
|
62 |
+
pathname = pathname.slice(pathname.indexOf('/') + 1);
|
63 |
+
}
|
64 |
+
else {
|
65 |
+
pathname = parentUrl.slice(8);
|
66 |
+
}
|
67 |
+
}
|
68 |
+
else {
|
69 |
+
// resolving to :/ so pathname is the /... part
|
70 |
+
pathname = parentUrl.slice(parentProtocol.length + (parentUrl[parentProtocol.length] === '/'));
|
71 |
+
}
|
72 |
+
|
73 |
+
if (relUrl[0] === '/')
|
74 |
+
return parentUrl.slice(0, parentUrl.length - pathname.length - 1) + relUrl;
|
75 |
+
|
76 |
+
// join together and split for removal of .. and . segments
|
77 |
+
// looping the string instead of anything fancy for perf reasons
|
78 |
+
// '../../../../../z' resolved to 'x/y' is just 'z'
|
79 |
+
const segmented = pathname.slice(0, pathname.lastIndexOf('/') + 1) + relUrl;
|
80 |
+
|
81 |
+
const output = [];
|
82 |
+
let segmentIndex = -1;
|
83 |
+
for (let i = 0; i < segmented.length; i++) {
|
84 |
+
// busy reading a segment - only terminate on '/'
|
85 |
+
if (segmentIndex !== -1) {
|
86 |
+
if (segmented[i] === '/') {
|
87 |
+
output.push(segmented.slice(segmentIndex, i + 1));
|
88 |
+
segmentIndex = -1;
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
// new segment - check if it is relative
|
93 |
+
else if (segmented[i] === '.') {
|
94 |
+
// ../ segment
|
95 |
+
if (segmented[i + 1] === '.' && (segmented[i + 2] === '/' || i + 2 === segmented.length)) {
|
96 |
+
output.pop();
|
97 |
+
i += 2;
|
98 |
+
}
|
99 |
+
// ./ segment
|
100 |
+
else if (segmented[i + 1] === '/' || i + 1 === segmented.length) {
|
101 |
+
i += 1;
|
102 |
+
}
|
103 |
+
else {
|
104 |
+
// the start of a new segment as below
|
105 |
+
segmentIndex = i;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
// it is the start of a new segment
|
109 |
+
else {
|
110 |
+
segmentIndex = i;
|
111 |
+
}
|
112 |
+
}
|
113 |
+
// finish reading out the last segment
|
114 |
+
if (segmentIndex !== -1)
|
115 |
+
output.push(segmented.slice(segmentIndex));
|
116 |
+
return parentUrl.slice(0, parentUrl.length - pathname.length) + output.join('');
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
/*
|
121 |
+
* Import maps implementation
|
122 |
+
*
|
123 |
+
* To make lookups fast we pre-resolve the entire import map
|
124 |
+
* and then match based on backtracked hash lookups
|
125 |
+
*
|
126 |
+
*/
|
127 |
+
function resolveUrl(relUrl, parentUrl) {
|
128 |
+
return resolveIfNotPlainOrUrl(relUrl, parentUrl) || (relUrl.indexOf(':') !== -1 ? relUrl : resolveIfNotPlainOrUrl('./' + relUrl, parentUrl));
|
129 |
+
}
|
130 |
+
|
131 |
+
function resolveAndComposePackages(packages, outPackages, baseUrl, parentMap) {
|
132 |
+
for (let p in packages) {
|
133 |
+
const resolvedLhs = resolveIfNotPlainOrUrl(p, baseUrl) || p;
|
134 |
+
if (outPackages[resolvedLhs]) {
|
135 |
+
throw new Error(`Dynamic import map rejected: Overrides entry "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
|
136 |
+
}
|
137 |
+
let target = packages[p];
|
138 |
+
if (typeof target !== 'string')
|
139 |
+
continue;
|
140 |
+
const mapped = resolveImportMap(parentMap, resolveIfNotPlainOrUrl(target, baseUrl) || target, baseUrl);
|
141 |
+
if (mapped) {
|
142 |
+
outPackages[resolvedLhs] = mapped;
|
143 |
+
continue;
|
144 |
+
}
|
145 |
+
targetWarning(p, packages[p], 'bare specifier did not resolve');
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
function resolveAndComposeImportMap(json, baseUrl, parentMap) {
|
150 |
+
const outMap = { imports: Object.assign({}, parentMap.imports), scopes: Object.assign({}, parentMap.scopes) };
|
151 |
+
|
152 |
+
if (json.imports)
|
153 |
+
resolveAndComposePackages(json.imports, outMap.imports, baseUrl, parentMap);
|
154 |
+
|
155 |
+
if (json.scopes)
|
156 |
+
for (let s in json.scopes) {
|
157 |
+
const resolvedScope = resolveUrl(s, baseUrl);
|
158 |
+
resolveAndComposePackages(json.scopes[s], outMap.scopes[resolvedScope] || (outMap.scopes[resolvedScope] = {}), baseUrl, parentMap);
|
159 |
+
}
|
160 |
+
|
161 |
+
return outMap;
|
162 |
+
}
|
163 |
+
|
164 |
+
function getMatch(path, matchObj) {
|
165 |
+
if (matchObj[path])
|
166 |
+
return path;
|
167 |
+
let sepIndex = path.length;
|
168 |
+
do {
|
169 |
+
const segment = path.slice(0, sepIndex + 1);
|
170 |
+
if (segment in matchObj)
|
171 |
+
return segment;
|
172 |
+
} while ((sepIndex = path.lastIndexOf('/', sepIndex - 1)) !== -1)
|
173 |
+
}
|
174 |
+
|
175 |
+
function applyPackages(id, packages) {
|
176 |
+
const pkgName = getMatch(id, packages);
|
177 |
+
if (pkgName) {
|
178 |
+
const pkg = packages[pkgName];
|
179 |
+
if (pkg === null) return;
|
180 |
+
if (id.length > pkgName.length && pkg[pkg.length - 1] !== '/')
|
181 |
+
targetWarning(pkgName, pkg, "should have a trailing '/'");
|
182 |
+
else
|
183 |
+
return pkg + id.slice(pkgName.length);
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
function targetWarning(match, target, msg) {
|
188 |
+
console.warn("Package target " + msg + ", resolving target '" + target + "' for " + match);
|
189 |
+
}
|
190 |
+
|
191 |
+
function resolveImportMap(importMap, resolvedOrPlain, parentUrl) {
|
192 |
+
let scopeUrl = parentUrl && getMatch(parentUrl, importMap.scopes);
|
193 |
+
while (scopeUrl) {
|
194 |
+
const packageResolution = applyPackages(resolvedOrPlain, importMap.scopes[scopeUrl]);
|
195 |
+
if (packageResolution)
|
196 |
+
return packageResolution;
|
197 |
+
scopeUrl = getMatch(scopeUrl.slice(0, scopeUrl.lastIndexOf('/')), importMap.scopes);
|
198 |
+
}
|
199 |
+
return applyPackages(resolvedOrPlain, importMap.imports) || resolvedOrPlain.indexOf(':') !== -1 && resolvedOrPlain;
|
200 |
+
}
|
201 |
+
|
202 |
+
const optionsScript = document.querySelector('script[type=esms-options]');
|
203 |
+
|
204 |
+
const esmsInitOptions = optionsScript ? JSON.parse(optionsScript.innerHTML) : self.esmsInitOptions ? self.esmsInitOptions : {};
|
205 |
+
|
206 |
+
let shimMode = !!esmsInitOptions.shimMode;
|
207 |
+
const resolveHook = globalHook(shimMode && esmsInitOptions.resolve);
|
208 |
+
|
209 |
+
const skip = esmsInitOptions.skip ? new RegExp(esmsInitOptions.skip) : null;
|
210 |
+
|
211 |
+
let nonce = esmsInitOptions.nonce;
|
212 |
+
|
213 |
+
if (!nonce) {
|
214 |
+
const nonceElement = document.querySelector('script[nonce]');
|
215 |
+
if (nonceElement)
|
216 |
+
nonce = nonceElement.nonce || nonceElement.getAttribute('nonce');
|
217 |
+
}
|
218 |
+
|
219 |
+
const onerror = globalHook(esmsInitOptions.onerror || noop);
|
220 |
+
const onpolyfill = globalHook(esmsInitOptions.onpolyfill || noop);
|
221 |
+
|
222 |
+
const { revokeBlobURLs, noLoadEventRetriggers } = esmsInitOptions;
|
223 |
+
|
224 |
+
const fetchHook = esmsInitOptions.fetch ? globalHook(esmsInitOptions.fetch) : fetch;
|
225 |
+
|
226 |
+
function globalHook(name) {
|
227 |
+
return typeof name === 'string' ? self[name] : name;
|
228 |
+
}
|
229 |
+
|
230 |
+
const enable = Array.isArray(esmsInitOptions.polyfillEnable) ? esmsInitOptions.polyfillEnable : [];
|
231 |
+
const cssModulesEnabled = enable.includes('css-modules');
|
232 |
+
const jsonModulesEnabled = enable.includes('json-modules');
|
233 |
+
|
234 |
+
function setShimMode() {
|
235 |
+
shimMode = true;
|
236 |
+
}
|
237 |
+
|
238 |
+
let err;
|
239 |
+
window.addEventListener('error', _err => err = _err);
|
240 |
+
function dynamicImportScript(url, { errUrl = url } = {}) {
|
241 |
+
err = undefined;
|
242 |
+
const src = createBlob(`import*as m from'${url}';self._esmsi=m`);
|
243 |
+
const s = Object.assign(document.createElement('script'), { type: 'module', src });
|
244 |
+
s.setAttribute('nonce', nonce);
|
245 |
+
s.setAttribute('noshim', '');
|
246 |
+
const p = new Promise((resolve, reject) => {
|
247 |
+
// Safari is unique in supporting module script error events
|
248 |
+
s.addEventListener('error', cb);
|
249 |
+
s.addEventListener('load', cb);
|
250 |
+
|
251 |
+
function cb(_err) {
|
252 |
+
document.head.removeChild(s);
|
253 |
+
if (self._esmsi) {
|
254 |
+
resolve(self._esmsi, baseUrl);
|
255 |
+
self._esmsi = undefined;
|
256 |
+
}
|
257 |
+
else {
|
258 |
+
reject(!(_err instanceof Event) && _err || err && err.error || new Error(`Error loading or executing the graph of ${errUrl} (check the console for ${src}).`));
|
259 |
+
err = undefined;
|
260 |
+
}
|
261 |
+
}
|
262 |
+
});
|
263 |
+
document.head.appendChild(s);
|
264 |
+
return p;
|
265 |
+
}
|
266 |
+
|
267 |
+
let dynamicImport = dynamicImportScript;
|
268 |
+
|
269 |
+
const supportsDynamicImportCheck = dynamicImportScript(createBlob('export default u=>import(u)')).then(_dynamicImport => {
|
270 |
+
if (_dynamicImport)
|
271 |
+
dynamicImport = _dynamicImport.default;
|
272 |
+
return !!_dynamicImport;
|
273 |
+
}, noop);
|
274 |
+
|
275 |
+
// support browsers without dynamic import support (eg Firefox 6x)
|
276 |
+
let supportsJsonAssertions = false;
|
277 |
+
let supportsCssAssertions = false;
|
278 |
+
|
279 |
+
let supportsImportMeta = false;
|
280 |
+
let supportsImportMaps = false;
|
281 |
+
|
282 |
+
let supportsDynamicImport = false;
|
283 |
+
|
284 |
+
const featureDetectionPromise = Promise.resolve(supportsDynamicImportCheck).then(_supportsDynamicImport => {
|
285 |
+
if (!_supportsDynamicImport)
|
286 |
+
return;
|
287 |
+
supportsDynamicImport = true;
|
288 |
+
|
289 |
+
return Promise.all([
|
290 |
+
dynamicImport(createBlob('import.meta')).then(() => supportsImportMeta = true, noop),
|
291 |
+
cssModulesEnabled && dynamicImport(createBlob('import"data:text/css,{}"assert{type:"css"}')).then(() => supportsCssAssertions = true, noop),
|
292 |
+
jsonModulesEnabled && dynamicImport(createBlob('import"data:text/json,{}"assert{type:"json"}')).then(() => supportsJsonAssertions = true, noop),
|
293 |
+
new Promise(resolve => {
|
294 |
+
self._$s = v => {
|
295 |
+
document.head.removeChild(iframe);
|
296 |
+
if (v) supportsImportMaps = true;
|
297 |
+
delete self._$s;
|
298 |
+
resolve();
|
299 |
+
};
|
300 |
+
const iframe = document.createElement('iframe');
|
301 |
+
iframe.style.display = 'none';
|
302 |
+
document.head.appendChild(iframe);
|
303 |
+
iframe.src = createBlob(`<script type=importmap nonce="${nonce}">{"imports":{"x":"data:text/javascript,"}}<${''}/script><script nonce="${nonce}">import('x').then(()=>1,()=>0).then(v=>parent._$s(v))<${''}/script>`, 'text/html');
|
304 |
+
})
|
305 |
+
]);
|
306 |
+
});
|
307 |
+
|
308 |
+
let e, r, a, i = 4194304; const s = 1 === new Uint8Array(new Uint16Array([1]).buffer)[0]; let t, f, c$1; function parse(k, l = "@") { if (t = k, f = l, t.length > i || !e) { for (; t.length > i;)i *= 2; r = new ArrayBuffer(4 * i), e = function (e, r, a) { "use asm"; var i = new e.Int8Array(a), s = new e.Int16Array(a), t = new e.Int32Array(a), f = new e.Uint8Array(a), c = new e.Uint16Array(a), n = 816; function b(e) { e = e | 0; var r = 0, a = 0, f = 0, b = 0, l = 0; l = n; n = n + 14336 | 0; b = l; i[589] = 1; s[291] = 0; s[292] = 0; s[293] = -1; t[15] = t[2]; i[590] = 0; t[14] = 0; i[588] = 0; t[16] = l + 10240; t[17] = l + 2048; i[591] = 0; e = (t[3] | 0) + -2 | 0; t[18] = e; r = e + (t[12] << 1) | 0; t[19] = r; e: while (1) { a = e + 2 | 0; t[18] = a; if (e >>> 0 >= r >>> 0) { f = 18; break } r: do { switch (s[a >> 1] | 0) { case 9: case 10: case 11: case 12: case 13: case 32: break; case 101: { if ((((s[292] | 0) == 0 ? R(a) | 0 : 0) ? B(e + 4 | 0, 120, 112, 111, 114, 116) | 0 : 0) ? (u(), (i[589] | 0) == 0) : 0) { f = 9; break e } else f = 17; break } case 105: { if (R(a) | 0 ? B(e + 4 | 0, 109, 112, 111, 114, 116) | 0 : 0) { k(); f = 17; } else f = 17; break } case 59: { f = 17; break } case 47: switch (s[e + 4 >> 1] | 0) { case 47: { G(); break r } case 42: { p(1); break r } default: { f = 16; break e } }default: { f = 16; break e } } } while (0); if ((f | 0) == 17) { f = 0; t[15] = t[18]; } e = t[18] | 0; r = t[19] | 0; } if ((f | 0) == 9) { e = t[18] | 0; t[15] = e; f = 19; } else if ((f | 0) == 16) { i[589] = 0; t[18] = e; f = 19; } else if ((f | 0) == 18) if (!(i[588] | 0)) { e = a; f = 19; } else e = 0; do { if ((f | 0) == 19) { e: while (1) { r = e + 2 | 0; t[18] = r; a = r; if (e >>> 0 >= (t[19] | 0) >>> 0) { f = 75; break } r: do { switch (s[r >> 1] | 0) { case 9: case 10: case 11: case 12: case 13: case 32: break; case 101: { if (((s[292] | 0) == 0 ? R(r) | 0 : 0) ? B(e + 4 | 0, 120, 112, 111, 114, 116) | 0 : 0) { u(); f = 74; } else f = 74; break } case 105: { if (R(r) | 0 ? B(e + 4 | 0, 109, 112, 111, 114, 116) | 0 : 0) { k(); f = 74; } else f = 74; break } case 99: { if ((R(r) | 0 ? z(e + 4 | 0, 108, 97, 115, 115) | 0 : 0) ? Z(s[e + 12 >> 1] | 0) | 0 : 0) { i[591] = 1; f = 74; } else f = 74; break } case 40: { r = t[15] | 0; a = t[17] | 0; f = s[292] | 0; s[292] = f + 1 << 16 >> 16; t[a + ((f & 65535) << 2) >> 2] = r; f = 74; break } case 41: { e = s[292] | 0; if (!(e << 16 >> 16)) { f = 36; break e } f = e + -1 << 16 >> 16; s[292] = f; e = t[11] | 0; if ((e | 0) != 0 ? (t[e + 20 >> 2] | 0) == (t[(t[17] | 0) + ((f & 65535) << 2) >> 2] | 0) : 0) { r = e + 4 | 0; if (!(t[r >> 2] | 0)) t[r >> 2] = a; t[e + 12 >> 2] = a; t[11] = 0; f = 74; } else f = 74; break } case 123: { f = t[15] | 0; a = t[8] | 0; e = f; do { if ((s[f >> 1] | 0) == 41 & (a | 0) != 0 ? (t[a + 4 >> 2] | 0) == (f | 0) : 0) { r = t[9] | 0; t[8] = r; if (!r) { t[4] = 0; break } else { t[r + 28 >> 2] = 0; break } } } while (0); r = s[292] | 0; f = r & 65535; i[b + f >> 0] = i[591] | 0; i[591] = 0; a = t[17] | 0; s[292] = r + 1 << 16 >> 16; t[a + (f << 2) >> 2] = e; f = 74; break } case 125: { e = s[292] | 0; if (!(e << 16 >> 16)) { f = 49; break e } a = e + -1 << 16 >> 16; s[292] = a; r = s[293] | 0; if (e << 16 >> 16 != r << 16 >> 16) if (r << 16 >> 16 != -1 & (a & 65535) < (r & 65535)) { f = 53; break e } else { f = 74; break r } else { a = t[16] | 0; f = (s[291] | 0) + -1 << 16 >> 16; s[291] = f; s[293] = s[a + ((f & 65535) << 1) >> 1] | 0; h(); f = 74; break r } } case 39: { d(39); f = 74; break } case 34: { d(34); f = 74; break } case 47: switch (s[e + 4 >> 1] | 0) { case 47: { G(); break r } case 42: { p(1); break r } default: { r = t[15] | 0; a = s[r >> 1] | 0; a: do { if (!(x(a) | 0)) { switch (a << 16 >> 16) { case 41: if (L(t[(t[17] | 0) + (c[292] << 2) >> 2] | 0) | 0) { f = 71; break a } else { f = 68; break a } case 125: break; default: { f = 68; break a } }e = c[292] | 0; if (!(y(t[(t[17] | 0) + (e << 2) >> 2] | 0) | 0) ? (i[b + e >> 0] | 0) == 0 : 0) f = 68; else f = 71; } else switch (a << 16 >> 16) { case 46: if (((s[r + -2 >> 1] | 0) + -48 & 65535) < 10) { f = 68; break a } else { f = 71; break a } case 43: if ((s[r + -2 >> 1] | 0) == 43) { f = 68; break a } else { f = 71; break a } case 45: if ((s[r + -2 >> 1] | 0) == 45) { f = 68; break a } else { f = 71; break a } default: { f = 71; break a } } } while (0); a: do { if ((f | 0) == 68) { f = 0; if (!(o(r) | 0)) { switch (a << 16 >> 16) { case 0: { f = 71; break a } case 47: break; default: { e = 1; break a } }if (!(i[590] | 0)) e = 1; else f = 71; } else f = 71; } } while (0); if ((f | 0) == 71) { I(); e = 0; } i[590] = e; f = 74; break r } }case 96: { h(); f = 74; break } default: f = 74; } } while (0); if ((f | 0) == 74) { f = 0; t[15] = t[18]; } e = t[18] | 0; } if ((f | 0) == 36) { Y(); e = 0; break } else if ((f | 0) == 49) { Y(); e = 0; break } else if ((f | 0) == 53) { Y(); e = 0; break } else if ((f | 0) == 75) { e = (s[293] | 0) == -1 & (s[292] | 0) == 0 & (i[588] | 0) == 0; break } } } while (0); n = l; return e | 0 } function u() { var e = 0, r = 0, a = 0, f = 0, c = 0, n = 0; c = t[18] | 0; n = c + 12 | 0; t[18] = n; r = w(1) | 0; e = t[18] | 0; if (!((e | 0) == (n | 0) ? !(S(r) | 0) : 0)) f = 3; e: do { if ((f | 0) == 3) { r: do { switch (r << 16 >> 16) { case 100: { J(e, e + 14 | 0); break e } case 97: { t[18] = e + 10; w(1) | 0; e = t[18] | 0; f = 6; break } case 102: { f = 6; break } case 99: { if (z(e + 2 | 0, 108, 97, 115, 115) | 0 ? (a = e + 10 | 0, F(s[a >> 1] | 0) | 0) : 0) { t[18] = a; c = w(1) | 0; n = t[18] | 0; H(c) | 0; J(n, t[18] | 0); t[18] = (t[18] | 0) + -2; break e } e = e + 4 | 0; t[18] = e; f = 13; break } case 108: case 118: { f = 13; break } case 123: { t[18] = e + 2; e = w(1) | 0; a = t[18] | 0; while (1) { if (_(e) | 0) { d(e); e = (t[18] | 0) + 2 | 0; t[18] = e; } else { H(e) | 0; e = t[18] | 0; } w(1) | 0; e = g(a, e) | 0; if (e << 16 >> 16 == 44) { t[18] = (t[18] | 0) + 2; e = w(1) | 0; } r = a; a = t[18] | 0; if (e << 16 >> 16 == 125) { f = 32; break } if ((a | 0) == (r | 0)) { f = 29; break } if (a >>> 0 > (t[19] | 0) >>> 0) { f = 31; break } } if ((f | 0) == 29) { Y(); break e } else if ((f | 0) == 31) { Y(); break e } else if ((f | 0) == 32) { t[18] = a + 2; f = 34; break r } break } case 42: { t[18] = e + 2; w(1) | 0; f = t[18] | 0; g(f, f) | 0; f = 34; break } default: { } } } while (0); if ((f | 0) == 6) { t[18] = e + 16; e = w(1) | 0; if (e << 16 >> 16 == 42) { t[18] = (t[18] | 0) + 2; e = w(1) | 0; } n = t[18] | 0; H(e) | 0; J(n, t[18] | 0); t[18] = (t[18] | 0) + -2; break } else if ((f | 0) == 13) { e = e + 4 | 0; t[18] = e; i[589] = 0; r: while (1) { t[18] = e + 2; n = w(1) | 0; e = t[18] | 0; switch ((H(n) | 0) << 16 >> 16) { case 91: case 123: { f = 15; break r } default: { } }r = t[18] | 0; if ((r | 0) == (e | 0)) break e; J(e, r); switch ((w(1) | 0) << 16 >> 16) { case 61: { f = 19; break r } case 44: break; default: { f = 20; break r } }e = t[18] | 0; } if ((f | 0) == 15) { t[18] = (t[18] | 0) + -2; break } else if ((f | 0) == 19) { t[18] = (t[18] | 0) + -2; break } else if ((f | 0) == 20) { t[18] = (t[18] | 0) + -2; break } } else if ((f | 0) == 34) r = w(1) | 0; e = t[18] | 0; if (r << 16 >> 16 == 102 ? K(e + 2 | 0, 114, 111, 109) | 0 : 0) { t[18] = e + 8; l(c, w(1) | 0); break } t[18] = e + -2; } } while (0); return } function k() { var e = 0, r = 0, a = 0, f = 0, c = 0; c = t[18] | 0; r = c + 12 | 0; t[18] = r; e: do { switch ((w(1) | 0) << 16 >> 16) { case 40: { r = t[17] | 0; a = s[292] | 0; s[292] = a + 1 << 16 >> 16; t[r + ((a & 65535) << 2) >> 2] = c; if ((s[t[15] >> 1] | 0) != 46) { v(c, (t[18] | 0) + 2 | 0, 0, c); t[11] = t[8]; t[18] = (t[18] | 0) + 2; switch ((w(1) | 0) << 16 >> 16) { case 39: { d(39); break } case 34: { d(34); break } default: { t[18] = (t[18] | 0) + -2; break e } }t[18] = (t[18] | 0) + 2; switch ((w(1) | 0) << 16 >> 16) { case 44: { c = t[18] | 0; t[(t[8] | 0) + 4 >> 2] = c; t[18] = c + 2; w(1) | 0; c = t[18] | 0; a = t[8] | 0; t[a + 16 >> 2] = c; i[a + 24 >> 0] = 1; t[18] = c + -2; break e } case 41: { s[292] = (s[292] | 0) + -1 << 16 >> 16; a = t[18] | 0; c = t[8] | 0; t[c + 4 >> 2] = a; t[c + 12 >> 2] = a; i[c + 24 >> 0] = 1; break e } default: { t[18] = (t[18] | 0) + -2; break e } } } break } case 46: { t[18] = (t[18] | 0) + 2; if (((w(1) | 0) << 16 >> 16 == 109 ? (e = t[18] | 0, K(e + 2 | 0, 101, 116, 97) | 0) : 0) ? (s[t[15] >> 1] | 0) != 46 : 0) v(c, c, e + 8 | 0, 2); break } case 42: case 39: case 34: { f = 16; break } case 123: { e = t[18] | 0; if (s[292] | 0) { t[18] = e + -2; break e } while (1) { if (e >>> 0 >= (t[19] | 0) >>> 0) break; e = w(1) | 0; if (!(_(e) | 0)) { if (e << 16 >> 16 == 125) { f = 31; break } } else d(e); e = (t[18] | 0) + 2 | 0; t[18] = e; } if ((f | 0) == 31) t[18] = (t[18] | 0) + 2; w(1) | 0; e = t[18] | 0; if (!(z(e, 102, 114, 111, 109) | 0)) { Y(); break e } t[18] = e + 8; e = w(1) | 0; if (_(e) | 0) { l(c, e); break e } else { Y(); break e } } default: if ((t[18] | 0) != (r | 0)) f = 16; } } while (0); do { if ((f | 0) == 16) { if (s[292] | 0) { t[18] = (t[18] | 0) + -2; break } e = t[19] | 0; r = t[18] | 0; while (1) { if (r >>> 0 >= e >>> 0) { f = 23; break } a = s[r >> 1] | 0; if (_(a) | 0) { f = 21; break } f = r + 2 | 0; t[18] = f; r = f; } if ((f | 0) == 21) { l(c, a); break } else if ((f | 0) == 23) { Y(); break } } } while (0); return } function l(e, r) { e = e | 0; r = r | 0; var a = 0, i = 0; a = (t[18] | 0) + 2 | 0; switch (r << 16 >> 16) { case 39: { d(39); i = 5; break } case 34: { d(34); i = 5; break } default: Y(); }do { if ((i | 0) == 5) { v(e, a, t[18] | 0, 1); t[18] = (t[18] | 0) + 2; i = (w(0) | 0) << 16 >> 16 == 97; r = t[18] | 0; if (i ? B(r + 2 | 0, 115, 115, 101, 114, 116) | 0 : 0) { t[18] = r + 12; if ((w(1) | 0) << 16 >> 16 != 123) { t[18] = r; break } e = t[18] | 0; a = e; e: while (1) { t[18] = a + 2; a = w(1) | 0; switch (a << 16 >> 16) { case 39: { d(39); t[18] = (t[18] | 0) + 2; a = w(1) | 0; break } case 34: { d(34); t[18] = (t[18] | 0) + 2; a = w(1) | 0; break } default: a = H(a) | 0; }if (a << 16 >> 16 != 58) { i = 16; break } t[18] = (t[18] | 0) + 2; switch ((w(1) | 0) << 16 >> 16) { case 39: { d(39); break } case 34: { d(34); break } default: { i = 20; break e } }t[18] = (t[18] | 0) + 2; switch ((w(1) | 0) << 16 >> 16) { case 125: { i = 25; break e } case 44: break; default: { i = 24; break e } }t[18] = (t[18] | 0) + 2; if ((w(1) | 0) << 16 >> 16 == 125) { i = 25; break } a = t[18] | 0; } if ((i | 0) == 16) { t[18] = r; break } else if ((i | 0) == 20) { t[18] = r; break } else if ((i | 0) == 24) { t[18] = r; break } else if ((i | 0) == 25) { i = t[8] | 0; t[i + 16 >> 2] = e; t[i + 12 >> 2] = (t[18] | 0) + 2; break } } t[18] = r + -2; } } while (0); return } function o(e) { e = e | 0; e: do { switch (s[e >> 1] | 0) { case 100: switch (s[e + -2 >> 1] | 0) { case 105: { e = q(e + -4 | 0, 118, 111) | 0; break e } case 108: { e = P(e + -4 | 0, 121, 105, 101) | 0; break e } default: { e = 0; break e } }case 101: { switch (s[e + -2 >> 1] | 0) { case 115: break; case 116: { e = E(e + -4 | 0, 100, 101, 108, 101) | 0; break e } default: { e = 0; break e } }switch (s[e + -4 >> 1] | 0) { case 108: { e = D(e + -6 | 0, 101) | 0; break e } case 97: { e = D(e + -6 | 0, 99) | 0; break e } default: { e = 0; break e } } } case 102: { if ((s[e + -2 >> 1] | 0) == 111 ? (s[e + -4 >> 1] | 0) == 101 : 0) switch (s[e + -6 >> 1] | 0) { case 99: { e = O(e + -8 | 0, 105, 110, 115, 116, 97, 110) | 0; break e } case 112: { e = q(e + -8 | 0, 116, 121) | 0; break e } default: { e = 0; break e } } else e = 0; break } case 110: { e = e + -2 | 0; if (D(e, 105) | 0) e = 1; else e = $(e, 114, 101, 116, 117, 114) | 0; break } case 111: { e = D(e + -2 | 0, 100) | 0; break } case 114: { e = m(e + -2 | 0, 100, 101, 98, 117, 103, 103, 101) | 0; break } case 116: { e = E(e + -2 | 0, 97, 119, 97, 105) | 0; break } case 119: switch (s[e + -2 >> 1] | 0) { case 101: { e = D(e + -4 | 0, 110) | 0; break e } case 111: { e = P(e + -4 | 0, 116, 104, 114) | 0; break e } default: { e = 0; break e } }default: e = 0; } } while (0); return e | 0 } function h() { var e = 0, r = 0, a = 0; r = t[19] | 0; a = t[18] | 0; e: while (1) { e = a + 2 | 0; if (a >>> 0 >= r >>> 0) { r = 8; break } switch (s[e >> 1] | 0) { case 96: { r = 9; break e } case 36: { if ((s[a + 4 >> 1] | 0) == 123) { r = 6; break e } break } case 92: { e = a + 4 | 0; break } default: { } }a = e; } if ((r | 0) == 6) { t[18] = a + 4; e = s[293] | 0; r = t[16] | 0; a = s[291] | 0; s[291] = a + 1 << 16 >> 16; s[r + ((a & 65535) << 1) >> 1] = e; a = (s[292] | 0) + 1 << 16 >> 16; s[292] = a; s[293] = a; } else if ((r | 0) == 8) { t[18] = e; Y(); } else if ((r | 0) == 9) t[18] = e; return } function w(e) { e = e | 0; var r = 0, a = 0, i = 0; a = t[18] | 0; e: do { r = s[a >> 1] | 0; r: do { if (r << 16 >> 16 != 47) if (e) if (Z(r) | 0) break; else break e; else if (Q(r) | 0) break; else break e; else switch (s[a + 2 >> 1] | 0) { case 47: { G(); break r } case 42: { p(e); break r } default: { r = 47; break e } } } while (0); i = t[18] | 0; a = i + 2 | 0; t[18] = a; } while (i >>> 0 < (t[19] | 0) >>> 0); return r | 0 } function d(e) { e = e | 0; var r = 0, a = 0, i = 0, f = 0; f = t[19] | 0; r = t[18] | 0; while (1) { i = r + 2 | 0; if (r >>> 0 >= f >>> 0) { r = 9; break } a = s[i >> 1] | 0; if (a << 16 >> 16 == e << 16 >> 16) { r = 10; break } if (a << 16 >> 16 == 92) { a = r + 4 | 0; if ((s[a >> 1] | 0) == 13) { r = r + 6 | 0; r = (s[r >> 1] | 0) == 10 ? r : a; } else r = a; } else if (ae(a) | 0) { r = 9; break } else r = i; } if ((r | 0) == 9) { t[18] = i; Y(); } else if ((r | 0) == 10) t[18] = i; return } function v(e, r, a, s) { e = e | 0; r = r | 0; a = a | 0; s = s | 0; var f = 0, c = 0; f = t[13] | 0; t[13] = f + 32; c = t[8] | 0; t[((c | 0) == 0 ? 16 : c + 28 | 0) >> 2] = f; t[9] = c; t[8] = f; t[f + 8 >> 2] = e; do { if (2 != (s | 0)) if (1 == (s | 0)) { t[f + 12 >> 2] = a + 2; break } else { t[f + 12 >> 2] = t[3]; break } else t[f + 12 >> 2] = a; } while (0); t[f >> 2] = r; t[f + 4 >> 2] = a; t[f + 16 >> 2] = 0; t[f + 20 >> 2] = s; i[f + 24 >> 0] = 1 == (s | 0) & 1; t[f + 28 >> 2] = 0; return } function A() { var e = 0, r = 0, a = 0; a = t[19] | 0; r = t[18] | 0; e: while (1) { e = r + 2 | 0; if (r >>> 0 >= a >>> 0) { r = 6; break } switch (s[e >> 1] | 0) { case 13: case 10: { r = 6; break e } case 93: { r = 7; break e } case 92: { e = r + 4 | 0; break } default: { } }r = e; } if ((r | 0) == 6) { t[18] = e; Y(); e = 0; } else if ((r | 0) == 7) { t[18] = e; e = 93; } return e | 0 } function C(e, r, a, i, t, f, c, n) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; t = t | 0; f = f | 0; c = c | 0; n = n | 0; if ((((((s[e + 12 >> 1] | 0) == n << 16 >> 16 ? (s[e + 10 >> 1] | 0) == c << 16 >> 16 : 0) ? (s[e + 8 >> 1] | 0) == f << 16 >> 16 : 0) ? (s[e + 6 >> 1] | 0) == t << 16 >> 16 : 0) ? (s[e + 4 >> 1] | 0) == i << 16 >> 16 : 0) ? (s[e + 2 >> 1] | 0) == a << 16 >> 16 : 0) r = (s[e >> 1] | 0) == r << 16 >> 16; else r = 0; return r | 0 } function y(e) { e = e | 0; switch (s[e >> 1] | 0) { case 62: { e = (s[e + -2 >> 1] | 0) == 61; break } case 41: case 59: { e = 1; break } case 104: { e = E(e + -2 | 0, 99, 97, 116, 99) | 0; break } case 121: { e = O(e + -2 | 0, 102, 105, 110, 97, 108, 108) | 0; break } case 101: { e = P(e + -2 | 0, 101, 108, 115) | 0; break } default: e = 0; }return e | 0 } function g(e, r) { e = e | 0; r = r | 0; var a = 0, i = 0; a = t[18] | 0; i = s[a >> 1] | 0; if (i << 16 >> 16 == 97) { t[18] = a + 4; a = w(1) | 0; e = t[18] | 0; if (_(a) | 0) { d(a); r = (t[18] | 0) + 2 | 0; t[18] = r; } else { H(a) | 0; r = t[18] | 0; } i = w(1) | 0; a = t[18] | 0; } if ((a | 0) != (e | 0)) J(e, r); return i | 0 } function I() { var e = 0, r = 0, a = 0; e: while (1) { e = t[18] | 0; r = e + 2 | 0; t[18] = r; if (e >>> 0 >= (t[19] | 0) >>> 0) { a = 7; break } switch (s[r >> 1] | 0) { case 13: case 10: { a = 7; break e } case 47: break e; case 91: { A() | 0; break } case 92: { t[18] = e + 4; break } default: { } } } if ((a | 0) == 7) Y(); return } function p(e) { e = e | 0; var r = 0, a = 0, i = 0, f = 0, c = 0; f = (t[18] | 0) + 2 | 0; t[18] = f; a = t[19] | 0; while (1) { r = f + 2 | 0; if (f >>> 0 >= a >>> 0) break; i = s[r >> 1] | 0; if (!e ? ae(i) | 0 : 0) break; if (i << 16 >> 16 == 42 ? (s[f + 4 >> 1] | 0) == 47 : 0) { c = 8; break } f = r; } if ((c | 0) == 8) { t[18] = r; r = f + 4 | 0; } t[18] = r; return } function U(e, r, a, i, t, f, c) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; t = t | 0; f = f | 0; c = c | 0; if (((((s[e + 10 >> 1] | 0) == c << 16 >> 16 ? (s[e + 8 >> 1] | 0) == f << 16 >> 16 : 0) ? (s[e + 6 >> 1] | 0) == t << 16 >> 16 : 0) ? (s[e + 4 >> 1] | 0) == i << 16 >> 16 : 0) ? (s[e + 2 >> 1] | 0) == a << 16 >> 16 : 0) r = (s[e >> 1] | 0) == r << 16 >> 16; else r = 0; return r | 0 } function m(e, r, a, i, f, c, n, b) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; f = f | 0; c = c | 0; n = n | 0; b = b | 0; var u = 0, k = 0; k = e + -12 | 0; u = t[3] | 0; if (k >>> 0 >= u >>> 0 ? C(k, r, a, i, f, c, n, b) | 0 : 0) if ((k | 0) == (u | 0)) u = 1; else u = F(s[e + -14 >> 1] | 0) | 0; else u = 0; return u | 0 } function S(e) { e = e | 0; e: do { switch (e << 16 >> 16) { case 38: case 37: case 33: { e = 1; break } default: if ((e & -8) << 16 >> 16 == 40 | (e + -58 & 65535) < 6) e = 1; else { switch (e << 16 >> 16) { case 91: case 93: case 94: { e = 1; break e } default: { } }e = (e + -123 & 65535) < 4; } } } while (0); return e | 0 } function x(e) { e = e | 0; e: do { switch (e << 16 >> 16) { case 38: case 37: case 33: break; default: if (!((e + -58 & 65535) < 6 | (e + -40 & 65535) < 7 & e << 16 >> 16 != 41)) { switch (e << 16 >> 16) { case 91: case 94: break e; default: { } }return e << 16 >> 16 != 125 & (e + -123 & 65535) < 4 | 0 } } } while (0); return 1 } function O(e, r, a, i, f, c, n) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; f = f | 0; c = c | 0; n = n | 0; var b = 0, u = 0; u = e + -10 | 0; b = t[3] | 0; if (u >>> 0 >= b >>> 0 ? U(u, r, a, i, f, c, n) | 0 : 0) if ((u | 0) == (b | 0)) b = 1; else b = F(s[e + -12 >> 1] | 0) | 0; else b = 0; return b | 0 } function $(e, r, a, i, f, c) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; f = f | 0; c = c | 0; var n = 0, b = 0; b = e + -8 | 0; n = t[3] | 0; if (b >>> 0 >= n >>> 0 ? B(b, r, a, i, f, c) | 0 : 0) if ((b | 0) == (n | 0)) n = 1; else n = F(s[e + -10 >> 1] | 0) | 0; else n = 0; return n | 0 } function j(e) { e = e | 0; var r = 0, a = 0, i = 0, f = 0; a = n; n = n + 16 | 0; i = a; t[i >> 2] = 0; t[12] = e; r = t[3] | 0; f = r + (e << 1) | 0; e = f + 2 | 0; s[f >> 1] = 0; t[i >> 2] = e; t[13] = e; t[4] = 0; t[8] = 0; t[6] = 0; t[5] = 0; t[10] = 0; t[7] = 0; n = a; return r | 0 } function B(e, r, a, i, t, f) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; t = t | 0; f = f | 0; if ((((s[e + 8 >> 1] | 0) == f << 16 >> 16 ? (s[e + 6 >> 1] | 0) == t << 16 >> 16 : 0) ? (s[e + 4 >> 1] | 0) == i << 16 >> 16 : 0) ? (s[e + 2 >> 1] | 0) == a << 16 >> 16 : 0) r = (s[e >> 1] | 0) == r << 16 >> 16; else r = 0; return r | 0 } function E(e, r, a, i, f) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; f = f | 0; var c = 0, n = 0; n = e + -6 | 0; c = t[3] | 0; if (n >>> 0 >= c >>> 0 ? z(n, r, a, i, f) | 0 : 0) if ((n | 0) == (c | 0)) c = 1; else c = F(s[e + -8 >> 1] | 0) | 0; else c = 0; return c | 0 } function P(e, r, a, i) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; var f = 0, c = 0; c = e + -4 | 0; f = t[3] | 0; if (c >>> 0 >= f >>> 0 ? K(c, r, a, i) | 0 : 0) if ((c | 0) == (f | 0)) f = 1; else f = F(s[e + -6 >> 1] | 0) | 0; else f = 0; return f | 0 } function q(e, r, a) { e = e | 0; r = r | 0; a = a | 0; var i = 0, f = 0; f = e + -2 | 0; i = t[3] | 0; if (f >>> 0 >= i >>> 0 ? N(f, r, a) | 0 : 0) if ((f | 0) == (i | 0)) i = 1; else i = F(s[e + -4 >> 1] | 0) | 0; else i = 0; return i | 0 } function z(e, r, a, i, t) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; t = t | 0; if (((s[e + 6 >> 1] | 0) == t << 16 >> 16 ? (s[e + 4 >> 1] | 0) == i << 16 >> 16 : 0) ? (s[e + 2 >> 1] | 0) == a << 16 >> 16 : 0) r = (s[e >> 1] | 0) == r << 16 >> 16; else r = 0; return r | 0 } function D(e, r) { e = e | 0; r = r | 0; var a = 0; a = t[3] | 0; if (a >>> 0 <= e >>> 0 ? (s[e >> 1] | 0) == r << 16 >> 16 : 0) if ((a | 0) == (e | 0)) a = 1; else a = F(s[e + -2 >> 1] | 0) | 0; else a = 0; return a | 0 } function F(e) { e = e | 0; e: do { if ((e + -9 & 65535) < 5) e = 1; else { switch (e << 16 >> 16) { case 32: case 160: { e = 1; break e } default: { } }e = e << 16 >> 16 != 46 & (S(e) | 0); } } while (0); return e | 0 } function G() { var e = 0, r = 0, a = 0; e = t[19] | 0; a = t[18] | 0; e: while (1) { r = a + 2 | 0; if (a >>> 0 >= e >>> 0) break; switch (s[r >> 1] | 0) { case 13: case 10: break e; default: a = r; } } t[18] = r; return } function H(e) { e = e | 0; while (1) { if (Z(e) | 0) break; if (S(e) | 0) break; e = (t[18] | 0) + 2 | 0; t[18] = e; e = s[e >> 1] | 0; if (!(e << 16 >> 16)) { e = 0; break } } return e | 0 } function J(e, r) { e = e | 0; r = r | 0; var a = 0, i = 0; a = t[13] | 0; t[13] = a + 12; i = t[10] | 0; t[((i | 0) == 0 ? 20 : i + 8 | 0) >> 2] = a; t[10] = a; t[a >> 2] = e; t[a + 4 >> 2] = r; t[a + 8 >> 2] = 0; return } function K(e, r, a, i) { e = e | 0; r = r | 0; a = a | 0; i = i | 0; if ((s[e + 4 >> 1] | 0) == i << 16 >> 16 ? (s[e + 2 >> 1] | 0) == a << 16 >> 16 : 0) r = (s[e >> 1] | 0) == r << 16 >> 16; else r = 0; return r | 0 } function L(e) { e = e | 0; if (!($(e, 119, 104, 105, 108, 101) | 0) ? !(P(e, 102, 111, 114) | 0) : 0) e = q(e, 105, 102) | 0; else e = 1; return e | 0 } function M() { var e = 0; e = t[(t[6] | 0) + 20 >> 2] | 0; switch (e | 0) { case 1: { e = -1; break } case 2: { e = -2; break } default: e = e - (t[3] | 0) >> 1; }return e | 0 } function N(e, r, a) { e = e | 0; r = r | 0; a = a | 0; if ((s[e + 2 >> 1] | 0) == a << 16 >> 16) r = (s[e >> 1] | 0) == r << 16 >> 16; else r = 0; return r | 0 } function Q(e) { e = e | 0; switch (e << 16 >> 16) { case 160: case 32: case 12: case 11: case 9: { e = 1; break } default: e = 0; }return e | 0 } function R(e) { e = e | 0; if ((t[3] | 0) == (e | 0)) e = 1; else e = F(s[e + -2 >> 1] | 0) | 0; return e | 0 } function T() { var e = 0; e = t[(t[6] | 0) + 16 >> 2] | 0; if (!e) e = -1; else e = e - (t[3] | 0) >> 1; return e | 0 } function V() { var e = 0; e = t[6] | 0; e = t[((e | 0) == 0 ? 16 : e + 28 | 0) >> 2] | 0; t[6] = e; return (e | 0) != 0 | 0 } function W() { var e = 0; e = t[7] | 0; e = t[((e | 0) == 0 ? 20 : e + 8 | 0) >> 2] | 0; t[7] = e; return (e | 0) != 0 | 0 } function X(e) { e = e | 0; var r = 0; r = n; n = n + e | 0; n = n + 15 & -16; return r | 0 } function Y() { i[588] = 1; t[14] = (t[18] | 0) - (t[3] | 0) >> 1; t[18] = (t[19] | 0) + 2; return } function Z(e) { e = e | 0; return (e | 128) << 16 >> 16 == 160 | (e + -9 & 65535) < 5 | 0 } function _(e) { e = e | 0; return e << 16 >> 16 == 39 | e << 16 >> 16 == 34 | 0 } function ee() { return (t[(t[6] | 0) + 12 >> 2] | 0) - (t[3] | 0) >> 1 | 0 } function re() { return (t[(t[6] | 0) + 8 >> 2] | 0) - (t[3] | 0) >> 1 | 0 } function ae(e) { e = e | 0; return e << 16 >> 16 == 13 | e << 16 >> 16 == 10 | 0 } function ie() { return (t[(t[6] | 0) + 4 >> 2] | 0) - (t[3] | 0) >> 1 | 0 } function se() { return (t[(t[7] | 0) + 4 >> 2] | 0) - (t[3] | 0) >> 1 | 0 } function te() { return (t[t[6] >> 2] | 0) - (t[3] | 0) >> 1 | 0 } function fe() { return (t[t[7] >> 2] | 0) - (t[3] | 0) >> 1 | 0 } function ce() { return f[(t[6] | 0) + 24 >> 0] | 0 | 0 } function ne(e) { e = e | 0; t[3] = e; return } function be() { return (i[589] | 0) != 0 | 0 } function ue() { return t[14] | 0 } return { ai: T, e: ue, ee: se, es: fe, f: be, id: M, ie: ie, ip: ce, is: te, p: b, re: W, ri: V, sa: j, se: ee, ses: ne, ss: re, sta: X } }({ Int8Array: Int8Array, Int16Array: Int16Array, Int32Array: Int32Array, Uint8Array: Uint8Array, Uint16Array: Uint16Array }, {}, r), a = e.sta(2 * i); } const o = t.length + 1; e.ses(a), e.sa(o - 1), (s ? b : n)(t, new Uint16Array(r, a, o)), e.p() || (c$1 = e.e(), h()); const w = [], d = []; for (; e.ri();) { const r = e.is(), a = e.ie(), i = e.ai(), s = e.id(), f = e.ss(), c = e.se(); let n; e.ip() && (n = u(-1 === s ? r : r + 1, t.charCodeAt(-1 === s ? r - 1 : r))), w.push({ n: n, s: r, e: a, ss: f, se: c, d: s, a: i }); } for (; e.re();) { const r = e.es(), a = t.charCodeAt(r); d.push(34 === a || 39 === a ? u(r + 1, a) : t.slice(e.es(), e.ee())); } return [w, d, !!e.f()] } function n(e, r) { const a = e.length; let i = 0; for (; i < a;) { const a = e.charCodeAt(i); r[i++] = (255 & a) << 8 | a >>> 8; } } function b(e, r) { const a = e.length; let i = 0; for (; i < a;)r[i] = e.charCodeAt(i++); } function u(e, r) { c$1 = e; let a = "", i = c$1; for (; ;) { c$1 >= t.length && h(); const e = t.charCodeAt(c$1); if (e === r) break; 92 === e ? (a += t.slice(i, c$1), a += k(), i = c$1) : (8232 === e || 8233 === e || o(e) && h(), ++c$1); } return a += t.slice(i, c$1++), a } function k() { let e = t.charCodeAt(++c$1); switch (++c$1, e) { case 110: return "\n"; case 114: return "\r"; case 120: return String.fromCharCode(l(2)); case 117: return function () { let e; 123 === t.charCodeAt(c$1) ? (++c$1, e = l(t.indexOf("}", c$1) - c$1), ++c$1, e > 1114111 && h()) : e = l(4); return e <= 65535 ? String.fromCharCode(e) : (e -= 65536, String.fromCharCode(55296 + (e >> 10), 56320 + (1023 & e))) }(); case 116: return "\t"; case 98: return "\b"; case 118: return "\v"; case 102: return "\f"; case 13: 10 === t.charCodeAt(c$1) && ++c$1; case 10: return ""; case 56: case 57: h(); default: if (e >= 48 && e <= 55) { let r = t.substr(c$1 - 1, 3).match(/^[0-7]+/)[0], a = parseInt(r, 8); return a > 255 && (r = r.slice(0, -1), a = parseInt(r, 8)), c$1 += r.length - 1, e = t.charCodeAt(c$1), "0" === r && 56 !== e && 57 !== e || h(), String.fromCharCode(a) } return o(e) ? "" : String.fromCharCode(e) } } function l(e) { const r = c$1; let a = 0, i = 0; for (let r = 0; r < e; ++r, ++c$1) { let e, s = t.charCodeAt(c$1); if (95 !== s) { if (s >= 97) e = s - 97 + 10; else if (s >= 65) e = s - 65 + 10; else { if (!(s >= 48 && s <= 57)) break; e = s - 48; } if (e >= 16) break; i = s, a = 16 * a + e; } else 95 !== i && 0 !== r || h(), i = s; } return 95 !== i && c$1 - r === e || h(), a } function o(e) { return 13 === e || 10 === e } function h() { throw Object.assign(new Error(`Parse error ${f}:${t.slice(0, c$1).split("\n").length}:${c$1 - t.lastIndexOf("\n", c$1 - 1)}`), { idx: c$1 }) }
|
309 |
+
|
310 |
+
async function defaultResolve(id, parentUrl) {
|
311 |
+
return resolveImportMap(importMap, resolveIfNotPlainOrUrl(id, parentUrl) || id, parentUrl);
|
312 |
+
}
|
313 |
+
|
314 |
+
async function _resolve(id, parentUrl) {
|
315 |
+
const urlResolved = resolveIfNotPlainOrUrl(id, parentUrl);
|
316 |
+
return {
|
317 |
+
r: resolveImportMap(importMap, urlResolved || id, parentUrl),
|
318 |
+
// b = bare specifier
|
319 |
+
b: !urlResolved && !isURL(id)
|
320 |
+
};
|
321 |
+
}
|
322 |
+
|
323 |
+
const resolve = resolveHook ? async (id, parentUrl) => ({ r: await resolveHook(id, parentUrl, defaultResolve), b: false }) : _resolve;
|
324 |
+
|
325 |
+
let id = 0;
|
326 |
+
const registry = {};
|
327 |
+
|
328 |
+
async function loadAll(load, seen) {
|
329 |
+
if (load.b || seen[load.u])
|
330 |
+
return;
|
331 |
+
seen[load.u] = 1;
|
332 |
+
await load.L;
|
333 |
+
await Promise.all(load.d.map(dep => loadAll(dep, seen)));
|
334 |
+
if (!load.n)
|
335 |
+
load.n = load.d.some(dep => dep.n);
|
336 |
+
}
|
337 |
+
|
338 |
+
let importMap = { imports: {}, scopes: {} };
|
339 |
+
let importMapSrcOrLazy = false;
|
340 |
+
let baselinePassthrough;
|
341 |
+
|
342 |
+
const initPromise = featureDetectionPromise.then(() => {
|
343 |
+
// shim mode is determined on initialization, no late shim mode
|
344 |
+
if (!shimMode) {
|
345 |
+
let seenScript = false;
|
346 |
+
for (const script of document.querySelectorAll('script[type="module-shim"],script[type="importmap-shim"],script[type="module"],script[type="importmap"]')) {
|
347 |
+
if (!seenScript && script.type === 'module')
|
348 |
+
seenScript = true;
|
349 |
+
if (script.type.endsWith('-shim')) {
|
350 |
+
setShimMode();
|
351 |
+
break;
|
352 |
+
}
|
353 |
+
if (seenScript && script.type === 'importmap') {
|
354 |
+
importMapSrcOrLazy = true;
|
355 |
+
break;
|
356 |
+
}
|
357 |
+
}
|
358 |
+
}
|
359 |
+
baselinePassthrough = supportsDynamicImport && supportsImportMeta && supportsImportMaps && (!jsonModulesEnabled || supportsJsonAssertions) && (!cssModulesEnabled || supportsCssAssertions) && !importMapSrcOrLazy && !false;
|
360 |
+
if (!baselinePassthrough) onpolyfill();
|
361 |
+
if (shimMode || !baselinePassthrough) {
|
362 |
+
new MutationObserver(mutations => {
|
363 |
+
for (const mutation of mutations) {
|
364 |
+
if (mutation.type !== 'childList') continue;
|
365 |
+
for (const node of mutation.addedNodes) {
|
366 |
+
if (node.tagName === 'SCRIPT') {
|
367 |
+
if (!shimMode && node.type === 'module' || shimMode && node.type === 'module-shim')
|
368 |
+
processScript(node);
|
369 |
+
if (!shimMode && node.type === 'importmap' || shimMode && node.type === 'importmap-shim')
|
370 |
+
processImportMap(node);
|
371 |
+
}
|
372 |
+
else if (node.tagName === 'LINK' && node.rel === 'modulepreload')
|
373 |
+
processPreload(node);
|
374 |
+
}
|
375 |
+
}
|
376 |
+
}).observe(document, { childList: true, subtree: true });
|
377 |
+
processImportMaps();
|
378 |
+
processScriptsAndPreloads();
|
379 |
+
return undefined;
|
380 |
+
}
|
381 |
+
});
|
382 |
+
let importMapPromise = initPromise;
|
383 |
+
|
384 |
+
let acceptingImportMaps = true;
|
385 |
+
async function topLevelLoad(url, fetchOpts, source, nativelyLoaded, lastStaticLoadPromise) {
|
386 |
+
if (!shimMode)
|
387 |
+
acceptingImportMaps = false;
|
388 |
+
await importMapPromise;
|
389 |
+
// early analysis opt-out - no need to even fetch if we have feature support
|
390 |
+
if (!shimMode && baselinePassthrough) {
|
391 |
+
// for polyfill case, only dynamic import needs a return value here, and dynamic import will never pass nativelyLoaded
|
392 |
+
if (nativelyLoaded)
|
393 |
+
return null;
|
394 |
+
await lastStaticLoadPromise;
|
395 |
+
return dynamicImport(source ? createBlob(source) : url, { errUrl: url || source });
|
396 |
+
}
|
397 |
+
const load = getOrCreateLoad(url, fetchOpts, source);
|
398 |
+
const seen = {};
|
399 |
+
await loadAll(load, seen);
|
400 |
+
lastLoad = undefined;
|
401 |
+
resolveDeps(load, seen);
|
402 |
+
await lastStaticLoadPromise;
|
403 |
+
if (source && !shimMode && !load.n && !false) {
|
404 |
+
const module = await dynamicImport(createBlob(source), { errUrl: source });
|
405 |
+
if (revokeBlobURLs) revokeObjectURLs(Object.keys(seen));
|
406 |
+
return module;
|
407 |
+
}
|
408 |
+
const module = await dynamicImport(!shimMode && !load.n && nativelyLoaded ? load.u : load.b, { errUrl: load.u });
|
409 |
+
// if the top-level load is a shell, run its update function
|
410 |
+
if (load.s)
|
411 |
+
(await dynamicImport(load.s)).u$_(module);
|
412 |
+
if (revokeBlobURLs) revokeObjectURLs(Object.keys(seen));
|
413 |
+
// when tla is supported, this should return the tla promise as an actual handle
|
414 |
+
// so readystate can still correspond to the sync subgraph exec completions
|
415 |
+
return module;
|
416 |
+
}
|
417 |
+
|
418 |
+
function revokeObjectURLs(registryKeys) {
|
419 |
+
let batch = 0;
|
420 |
+
const keysLength = registryKeys.length;
|
421 |
+
const schedule = self.requestIdleCallback ? self.requestIdleCallback : self.requestAnimationFrame;
|
422 |
+
schedule(cleanup);
|
423 |
+
function cleanup() {
|
424 |
+
const batchStartIndex = batch * 100;
|
425 |
+
if (batchStartIndex > keysLength) return
|
426 |
+
for (const key of registryKeys.slice(batchStartIndex, batchStartIndex + 100)) {
|
427 |
+
const load = registry[key];
|
428 |
+
if (load) URL.revokeObjectURL(load.b);
|
429 |
+
}
|
430 |
+
batch++;
|
431 |
+
schedule(cleanup);
|
432 |
+
}
|
433 |
+
}
|
434 |
+
|
435 |
+
async function importShim(id, parentUrl = baseUrl, _assertion) {
|
436 |
+
// needed for shim check
|
437 |
+
await initPromise;
|
438 |
+
if (acceptingImportMaps || shimMode || !baselinePassthrough) {
|
439 |
+
processImportMaps();
|
440 |
+
if (!shimMode)
|
441 |
+
acceptingImportMaps = false;
|
442 |
+
}
|
443 |
+
await importMapPromise;
|
444 |
+
return topLevelLoad((await resolve(id, parentUrl)).r || throwUnresolved(id, parentUrl), { credentials: 'same-origin' });
|
445 |
+
}
|
446 |
+
|
447 |
+
self.importShim = importShim;
|
448 |
+
|
449 |
+
if (shimMode) {
|
450 |
+
importShim.getImportMap = () => JSON.parse(JSON.stringify(importMap));
|
451 |
+
}
|
452 |
+
|
453 |
+
const meta = {};
|
454 |
+
|
455 |
+
async function importMetaResolve(id, parentUrl = this.url) {
|
456 |
+
return (await resolve(id, `${parentUrl}`)).r || throwUnresolved(id, parentUrl);
|
457 |
+
}
|
458 |
+
|
459 |
+
self._esmsm = meta;
|
460 |
+
|
461 |
+
function urlJsString(url) {
|
462 |
+
return `'${url.replace(/'/g, "\\'")}'`;
|
463 |
+
}
|
464 |
+
|
465 |
+
let lastLoad;
|
466 |
+
function resolveDeps(load, seen) {
|
467 |
+
if (load.b || !seen[load.u])
|
468 |
+
return;
|
469 |
+
seen[load.u] = 0;
|
470 |
+
|
471 |
+
for (const dep of load.d)
|
472 |
+
resolveDeps(dep, seen);
|
473 |
+
|
474 |
+
const [imports] = load.a;
|
475 |
+
|
476 |
+
// "execution"
|
477 |
+
const source = load.S;
|
478 |
+
|
479 |
+
// edge doesnt execute sibling in order, so we fix this up by ensuring all previous executions are explicit dependencies
|
480 |
+
let resolvedSource = edge && lastLoad ? `import '${lastLoad}';` : '';
|
481 |
+
|
482 |
+
if (!imports.length) {
|
483 |
+
resolvedSource += source;
|
484 |
+
}
|
485 |
+
else {
|
486 |
+
// once all deps have loaded we can inline the dependency resolution blobs
|
487 |
+
// and define this blob
|
488 |
+
let lastIndex = 0, depIndex = 0;
|
489 |
+
for (const { s: start, se: end, d: dynamicImportIndex } of imports) {
|
490 |
+
// dependency source replacements
|
491 |
+
if (dynamicImportIndex === -1) {
|
492 |
+
const depLoad = load.d[depIndex++];
|
493 |
+
let blobUrl = depLoad.b;
|
494 |
+
if (!blobUrl) {
|
495 |
+
// circular shell creation
|
496 |
+
if (!(blobUrl = depLoad.s)) {
|
497 |
+
blobUrl = depLoad.s = createBlob(`export function u$_(m){${depLoad.a[1].map(
|
498 |
+
name => name === 'default' ? `$_default=m.default` : `${name}=m.${name}`
|
499 |
+
).join(',')
|
500 |
+
}}${depLoad.a[1].map(name =>
|
501 |
+
name === 'default' ? `let $_default;export{$_default as default}` : `export let ${name}`
|
502 |
+
).join(';')
|
503 |
+
}\n//# sourceURL=${depLoad.r}?cycle`);
|
504 |
+
}
|
505 |
+
}
|
506 |
+
// circular shell execution
|
507 |
+
else if (depLoad.s) {
|
508 |
+
resolvedSource += `${source.slice(lastIndex, start - 1)}/*${source.slice(start - 1, end)}*/${urlJsString(blobUrl)};import*as m$_${depIndex} from'${depLoad.b}';import{u$_ as u$_${depIndex}}from'${depLoad.s}';u$_${depIndex}(m$_${depIndex})`;
|
509 |
+
lastIndex = end;
|
510 |
+
depLoad.s = undefined;
|
511 |
+
continue;
|
512 |
+
}
|
513 |
+
resolvedSource += `${source.slice(lastIndex, start - 1)}/*${source.slice(start - 1, end)}*/${urlJsString(blobUrl)}`;
|
514 |
+
lastIndex = end;
|
515 |
+
}
|
516 |
+
// import.meta
|
517 |
+
else if (dynamicImportIndex === -2) {
|
518 |
+
meta[load.r] = { url: load.r, resolve: importMetaResolve };
|
519 |
+
resolvedSource += `${source.slice(lastIndex, start)}self._esmsm[${urlJsString(load.r)}]`;
|
520 |
+
lastIndex = end;
|
521 |
+
}
|
522 |
+
// dynamic import
|
523 |
+
else {
|
524 |
+
resolvedSource += `${source.slice(lastIndex, dynamicImportIndex + 6)}Shim(${source.slice(start, end)}, ${load.r && urlJsString(load.r)}`;
|
525 |
+
lastIndex = end;
|
526 |
+
}
|
527 |
+
}
|
528 |
+
|
529 |
+
resolvedSource += source.slice(lastIndex);
|
530 |
+
}
|
531 |
+
|
532 |
+
// ; and // trailer support added for Ruby 7 source maps compatibility
|
533 |
+
let hasSourceURL = false;
|
534 |
+
resolvedSource = resolvedSource.replace(sourceMapURLRegEx, (match, isMapping, url) => (hasSourceURL = !isMapping, match.replace(url, () => new URL(url, load.r))));
|
535 |
+
if (!hasSourceURL)
|
536 |
+
resolvedSource += '\n//# sourceURL=' + load.r;
|
537 |
+
|
538 |
+
load.b = lastLoad = createBlob(resolvedSource);
|
539 |
+
load.S = undefined;
|
540 |
+
}
|
541 |
+
|
542 |
+
const sourceMapURLRegEx = /\n\/\/# source(Mapping)?URL=([^\n]+)\s*((;|\/\/[^#][^\n]*)\s*)*$/;
|
543 |
+
|
544 |
+
const jsContentType = /^(text|application)\/(x-)?javascript(;|$)/;
|
545 |
+
const jsonContentType = /^(text|application)\/json(;|$)/;
|
546 |
+
const cssContentType = /^(text|application)\/css(;|$)/;
|
547 |
+
const wasmContentType = /^application\/wasm(;|$)/;
|
548 |
+
|
549 |
+
const cssUrlRegEx = /url\(\s*(?:(["'])((?:\\.|[^\n\\"'])+)\1|((?:\\.|[^\s,"'()\\])+))\s*\)/g;
|
550 |
+
|
551 |
+
// restrict in-flight fetches to a pool of 100
|
552 |
+
let p = [];
|
553 |
+
let c = 0;
|
554 |
+
function pushFetchPool() {
|
555 |
+
if (++c > 100)
|
556 |
+
return new Promise(r => p.push(r));
|
557 |
+
}
|
558 |
+
function popFetchPool() {
|
559 |
+
c--;
|
560 |
+
if (p.length)
|
561 |
+
p.shift()();
|
562 |
+
}
|
563 |
+
|
564 |
+
async function doFetch(url, fetchOpts) {
|
565 |
+
const poolQueue = pushFetchPool();
|
566 |
+
if (poolQueue) await poolQueue;
|
567 |
+
try {
|
568 |
+
var res = await fetchHook(url, fetchOpts);
|
569 |
+
}
|
570 |
+
finally {
|
571 |
+
popFetchPool();
|
572 |
+
}
|
573 |
+
if (!res.ok)
|
574 |
+
throw new Error(`${res.status} ${res.statusText} ${res.url}`);
|
575 |
+
const contentType = res.headers.get('content-type');
|
576 |
+
if (jsContentType.test(contentType))
|
577 |
+
return { r: res.url, s: await res.text(), t: 'js' };
|
578 |
+
else if (jsonContentType.test(contentType))
|
579 |
+
return { r: res.url, s: `export default ${await res.text()}`, t: 'json' };
|
580 |
+
else if (cssContentType.test(contentType))
|
581 |
+
return {
|
582 |
+
r: res.url, s: `var s=new CSSStyleSheet();s.replaceSync(${JSON.stringify((await res.text()).replace(cssUrlRegEx, (_match, quotes, relUrl1, relUrl2) => `url(${quotes}${resolveUrl(relUrl1 || relUrl2, url)}${quotes})`))
|
583 |
+
});export default s;`, t: 'css'
|
584 |
+
};
|
585 |
+
else if (wasmContentType.test(contentType))
|
586 |
+
throw new Error('WASM modules not supported');
|
587 |
+
else
|
588 |
+
throw new Error(`Unknown Content-Type "${contentType}"`);
|
589 |
+
}
|
590 |
+
|
591 |
+
function getOrCreateLoad(url, fetchOpts, source) {
|
592 |
+
let load = registry[url];
|
593 |
+
if (load)
|
594 |
+
return load;
|
595 |
+
|
596 |
+
load = registry[url] = {
|
597 |
+
// url
|
598 |
+
u: url,
|
599 |
+
// response url
|
600 |
+
r: undefined,
|
601 |
+
// fetchPromise
|
602 |
+
f: undefined,
|
603 |
+
// source
|
604 |
+
S: undefined,
|
605 |
+
// linkPromise
|
606 |
+
L: undefined,
|
607 |
+
// analysis
|
608 |
+
a: undefined,
|
609 |
+
// deps
|
610 |
+
d: undefined,
|
611 |
+
// blobUrl
|
612 |
+
b: undefined,
|
613 |
+
// shellUrl
|
614 |
+
s: undefined,
|
615 |
+
// needsShim
|
616 |
+
n: false,
|
617 |
+
// type
|
618 |
+
t: null
|
619 |
+
};
|
620 |
+
|
621 |
+
load.f = (async () => {
|
622 |
+
if (!source) {
|
623 |
+
// preload fetch options override fetch options (race)
|
624 |
+
let t;
|
625 |
+
({ r: load.r, s: source, t } = await (fetchCache[url] || doFetch(url, fetchOpts)));
|
626 |
+
if (t && !shimMode) {
|
627 |
+
if (t === 'css' && !cssModulesEnabled || t === 'json' && !jsonModulesEnabled)
|
628 |
+
throw new Error(`${t}-modules require <script type="esms-options">{ "polyfillEnable": ["${t}-modules"] }<${''}/script>`);
|
629 |
+
if (t === 'css' && !supportsCssAssertions || t === 'json' && !supportsJsonAssertions)
|
630 |
+
load.n = true;
|
631 |
+
}
|
632 |
+
}
|
633 |
+
try {
|
634 |
+
load.a = parse(source, load.u);
|
635 |
+
}
|
636 |
+
catch (e) {
|
637 |
+
console.warn(e);
|
638 |
+
load.a = [[], []];
|
639 |
+
}
|
640 |
+
load.S = source;
|
641 |
+
return load;
|
642 |
+
})();
|
643 |
+
|
644 |
+
load.L = load.f.then(async () => {
|
645 |
+
let childFetchOpts = fetchOpts;
|
646 |
+
load.d = (await Promise.all(load.a[0].map(async ({ n, d }) => {
|
647 |
+
if (d >= 0 && !supportsDynamicImport || d === 2 && !supportsImportMeta)
|
648 |
+
load.n = true;
|
649 |
+
if (!n) return;
|
650 |
+
const { r, b } = await resolve(n, load.r || load.u);
|
651 |
+
if (b && (!supportsImportMaps || importMapSrcOrLazy))
|
652 |
+
load.n = true;
|
653 |
+
if (d !== -1) return;
|
654 |
+
if (!r)
|
655 |
+
throwUnresolved(n, load.r || load.u);
|
656 |
+
if (skip && skip.test(r)) return { b: r };
|
657 |
+
if (childFetchOpts.integrity)
|
658 |
+
childFetchOpts = Object.assign({}, childFetchOpts, { integrity: undefined });
|
659 |
+
return getOrCreateLoad(r, childFetchOpts).f;
|
660 |
+
}))).filter(l => l);
|
661 |
+
});
|
662 |
+
|
663 |
+
return load;
|
664 |
+
}
|
665 |
+
|
666 |
+
function processScriptsAndPreloads() {
|
667 |
+
for (const script of document.querySelectorAll(shimMode ? 'script[type="module-shim"]' : 'script[type="module"]'))
|
668 |
+
processScript(script);
|
669 |
+
for (const link of document.querySelectorAll('link[rel="modulepreload"]'))
|
670 |
+
processPreload(link);
|
671 |
+
}
|
672 |
+
|
673 |
+
function processImportMaps() {
|
674 |
+
for (const script of document.querySelectorAll(shimMode ? 'script[type="importmap-shim"]' : 'script[type="importmap"]'))
|
675 |
+
processImportMap(script);
|
676 |
+
}
|
677 |
+
|
678 |
+
function getFetchOpts(script) {
|
679 |
+
const fetchOpts = {};
|
680 |
+
if (script.integrity)
|
681 |
+
fetchOpts.integrity = script.integrity;
|
682 |
+
if (script.referrerpolicy)
|
683 |
+
fetchOpts.referrerPolicy = script.referrerpolicy;
|
684 |
+
if (script.crossorigin === 'use-credentials')
|
685 |
+
fetchOpts.credentials = 'include';
|
686 |
+
else if (script.crossorigin === 'anonymous')
|
687 |
+
fetchOpts.credentials = 'omit';
|
688 |
+
else
|
689 |
+
fetchOpts.credentials = 'same-origin';
|
690 |
+
return fetchOpts;
|
691 |
+
}
|
692 |
+
|
693 |
+
let lastStaticLoadPromise = Promise.resolve();
|
694 |
+
|
695 |
+
let domContentLoadedCnt = 1;
|
696 |
+
function domContentLoadedCheck() {
|
697 |
+
if (--domContentLoadedCnt === 0 && !noLoadEventRetriggers)
|
698 |
+
document.dispatchEvent(new Event('DOMContentLoaded'));
|
699 |
+
}
|
700 |
+
// this should always trigger because we assume es-module-shims is itself a domcontentloaded requirement
|
701 |
+
document.addEventListener('DOMContentLoaded', async () => {
|
702 |
+
await initPromise;
|
703 |
+
domContentLoadedCheck();
|
704 |
+
if (shimMode || !baselinePassthrough) {
|
705 |
+
processImportMaps();
|
706 |
+
processScriptsAndPreloads();
|
707 |
+
}
|
708 |
+
});
|
709 |
+
|
710 |
+
let readyStateCompleteCnt = 1;
|
711 |
+
if (document.readyState === 'complete') {
|
712 |
+
readyStateCompleteCheck();
|
713 |
+
}
|
714 |
+
else {
|
715 |
+
document.addEventListener('readystatechange', async () => {
|
716 |
+
processImportMaps();
|
717 |
+
await initPromise;
|
718 |
+
readyStateCompleteCheck();
|
719 |
+
});
|
720 |
+
}
|
721 |
+
function readyStateCompleteCheck() {
|
722 |
+
if (--readyStateCompleteCnt === 0 && !noLoadEventRetriggers)
|
723 |
+
document.dispatchEvent(new Event('readystatechange'));
|
724 |
+
}
|
725 |
+
|
726 |
+
function processImportMap(script) {
|
727 |
+
if (script.ep) // ep marker = script processed
|
728 |
+
return;
|
729 |
+
// empty inline scripts sometimes show before domready
|
730 |
+
if (!script.src && !script.innerHTML)
|
731 |
+
return;
|
732 |
+
script.ep = true;
|
733 |
+
// we dont currently support multiple, external or dynamic imports maps in polyfill mode to match native
|
734 |
+
if (script.src) {
|
735 |
+
if (!shimMode)
|
736 |
+
return;
|
737 |
+
importMapSrcOrLazy = true;
|
738 |
+
}
|
739 |
+
if (acceptingImportMaps) {
|
740 |
+
importMapPromise = importMapPromise
|
741 |
+
.then(async () => {
|
742 |
+
importMap = resolveAndComposeImportMap(script.src ? await (await fetchHook(script.src)).json() : JSON.parse(script.innerHTML), script.src || baseUrl, importMap);
|
743 |
+
})
|
744 |
+
.catch(error => setTimeout(() => { throw error }));
|
745 |
+
if (!shimMode)
|
746 |
+
acceptingImportMaps = false;
|
747 |
+
}
|
748 |
+
}
|
749 |
+
|
750 |
+
function processScript(script) {
|
751 |
+
if (script.ep) // ep marker = script processed
|
752 |
+
return;
|
753 |
+
if (script.getAttribute('noshim') !== null)
|
754 |
+
return;
|
755 |
+
// empty inline scripts sometimes show before domready
|
756 |
+
if (!script.src && !script.innerHTML)
|
757 |
+
return;
|
758 |
+
script.ep = true;
|
759 |
+
// does this load block readystate complete
|
760 |
+
const isReadyScript = readyStateCompleteCnt > 0;
|
761 |
+
// does this load block DOMContentLoaded
|
762 |
+
const isDomContentLoadedScript = domContentLoadedCnt > 0;
|
763 |
+
if (isReadyScript) readyStateCompleteCnt++;
|
764 |
+
if (isDomContentLoadedScript) domContentLoadedCnt++;
|
765 |
+
const blocks = script.getAttribute('async') === null && isReadyScript;
|
766 |
+
const loadPromise = topLevelLoad(script.src || `${baseUrl}?${id++}`, getFetchOpts(script), !script.src && script.innerHTML, !shimMode, blocks && lastStaticLoadPromise).catch(e => {
|
767 |
+
setTimeout(() => { throw e });
|
768 |
+
onerror(e);
|
769 |
+
});
|
770 |
+
if (blocks)
|
771 |
+
lastStaticLoadPromise = loadPromise.then(readyStateCompleteCheck);
|
772 |
+
if (isDomContentLoadedScript)
|
773 |
+
loadPromise.then(domContentLoadedCheck);
|
774 |
+
}
|
775 |
+
|
776 |
+
const fetchCache = {};
|
777 |
+
function processPreload(link) {
|
778 |
+
if (link.ep) // ep marker = processed
|
779 |
+
return;
|
780 |
+
link.ep = true;
|
781 |
+
if (fetchCache[link.href])
|
782 |
+
return;
|
783 |
+
fetchCache[link.href] = doFetch(link.href, getFetchOpts(link));
|
784 |
+
}
|
785 |
+
|
786 |
+
function throwUnresolved(id, parentUrl) {
|
787 |
+
throw Error("Unable to resolve specifier '" + id + (parentUrl ? "' from " + parentUrl : "'"));
|
788 |
+
}
|
789 |
+
|
790 |
+
})();
|
posex/js/posex.js
ADDED
@@ -0,0 +1,844 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
async function _import() {
|
2 |
+
if (!globalThis.posex || !globalThis.posex.import) {
|
3 |
+
const THREE = await import('three');
|
4 |
+
const { TrackballControls } = await import('three-trackballcontrols');
|
5 |
+
const { DragControls } = await import('three-dragcontrols');
|
6 |
+
const { MeshLine, MeshLineMaterial } = await import('three-meshline');
|
7 |
+
return { THREE, TrackballControls, DragControls, MeshLine, MeshLineMaterial };
|
8 |
+
} else {
|
9 |
+
return await globalThis.posex.import();
|
10 |
+
}
|
11 |
+
}
|
12 |
+
const { THREE, TrackballControls, DragControls, MeshLine, MeshLineMaterial } = await _import();
|
13 |
+
|
14 |
+
const JOINT_RADIUS = 4.0;
|
15 |
+
const LIMB_SIZE = 4.0;
|
16 |
+
const LIMB_N = 64;
|
17 |
+
|
18 |
+
const joint_names = [
|
19 |
+
'nose',
|
20 |
+
'neck',
|
21 |
+
'right shoulder',
|
22 |
+
'right elbow',
|
23 |
+
'right wrist',
|
24 |
+
'left shoulder',
|
25 |
+
'left elbow',
|
26 |
+
'left wrist',
|
27 |
+
'right hip',
|
28 |
+
'right knee',
|
29 |
+
'right ancle',
|
30 |
+
'left hip',
|
31 |
+
'left knee',
|
32 |
+
'left ancle',
|
33 |
+
'right eye',
|
34 |
+
'left eye',
|
35 |
+
'right ear',
|
36 |
+
'left ear'
|
37 |
+
];
|
38 |
+
|
39 |
+
const joint_colors = [
|
40 |
+
// r g b
|
41 |
+
[255, 0, 0], // 0: nose
|
42 |
+
[255, 85, 0], // 1: neck
|
43 |
+
[255, 170, 0], // 2: right shoulder
|
44 |
+
[255, 255, 0], // 3: right elbow
|
45 |
+
[170, 255, 0], // 4: right wrist
|
46 |
+
[85, 255, 0], // 5: left shoulder
|
47 |
+
[0, 255, 0], // 6: left elbow
|
48 |
+
[0, 255, 85], // 7: left wrist
|
49 |
+
[0, 255, 170], // 8: right hip
|
50 |
+
[0, 255, 255], // 9: right knee
|
51 |
+
[0, 170, 255], // 10: right ancle
|
52 |
+
[0, 85, 255], // 11: left hip
|
53 |
+
[0, 0, 255], // 12: left knee
|
54 |
+
[85, 0, 255], // 13: left ancle
|
55 |
+
[170, 0, 255], // 14: right eye
|
56 |
+
[255, 0, 255], // 15: left eye
|
57 |
+
[255, 0, 170], // 16: right ear
|
58 |
+
[255, 0, 85] // 17: left ear
|
59 |
+
];
|
60 |
+
|
61 |
+
const limb_pairs = [
|
62 |
+
[1, 2], // 0: right shoulder
|
63 |
+
[1, 5], // 1: left shoulder
|
64 |
+
[2, 3], // 2: right upper arm
|
65 |
+
[3, 4], // 3: right forearm
|
66 |
+
[5, 6], // 4: left upper arm
|
67 |
+
[6, 7], // 5: left forearm
|
68 |
+
[1, 8], // 6: right hip
|
69 |
+
[8, 9], // 7: right upper leg
|
70 |
+
[9, 10], // 8: right lower leg
|
71 |
+
[1, 11], // 9: left hip
|
72 |
+
[11, 12], // 10: left upper leg
|
73 |
+
[12, 13], // 11: left lower leg
|
74 |
+
[1, 0], // 12: neck
|
75 |
+
[0, 14], // 13: right eye
|
76 |
+
[14, 16], // 14: right ear
|
77 |
+
[0, 15], // 15: left eye
|
78 |
+
[15, 17], // 16: left ear
|
79 |
+
];
|
80 |
+
|
81 |
+
const standard_pose = [
|
82 |
+
// x y z ∈ [0,1]
|
83 |
+
[0.500, 0.820, 0.000], // 0: nose
|
84 |
+
[0.500, 0.750, 0.000], // 1: neck
|
85 |
+
[0.416, 0.750, 0.000], // 2: right shoulder
|
86 |
+
[0.305, 0.750, 0.000], // 3: right elbow
|
87 |
+
[0.188, 0.750, 0.000], // 4: right wrist
|
88 |
+
[0.584, 0.750, 0.000], // 5: left shoulder
|
89 |
+
[0.695, 0.750, 0.000], // 6: left elbow
|
90 |
+
[0.812, 0.750, 0.000], // 7: left wrist
|
91 |
+
[0.447, 0.511, 0.000], // 8: right hip
|
92 |
+
[0.453, 0.295, 0.000], // 9: right knee
|
93 |
+
[0.445, 0.109, 0.000], // 10: right ancle
|
94 |
+
[0.553, 0.511, 0.000], // 11: left hip
|
95 |
+
[0.547, 0.295, 0.000], // 12: left knee
|
96 |
+
[0.555, 0.109, 0.000], // 13: left ancle
|
97 |
+
[0.480, 0.848, 0.000], // 14: right eye
|
98 |
+
[0.520, 0.848, 0.000], // 15: left eye
|
99 |
+
[0.450, 0.834, 0.000], // 16: right ear
|
100 |
+
[0.550, 0.834, 0.000] // 17: left ear
|
101 |
+
]
|
102 |
+
|
103 |
+
for (let xyz of standard_pose) {
|
104 |
+
xyz[0] = xyz[0] - 0.5; // [0,1] -> [-0.5,0.5]
|
105 |
+
xyz[1] = xyz[1] - 0.5; // [0,1] -> [-0.5,0.5]
|
106 |
+
//xyz[2] = xyz[2] * 2 - 1.0;
|
107 |
+
}
|
108 |
+
|
109 |
+
function create_body(unit, x0, y0, z0) {
|
110 |
+
const joints = [];
|
111 |
+
const limbs = [];
|
112 |
+
|
113 |
+
for (let i = 0; i < standard_pose.length; ++i) {
|
114 |
+
const [x, y, z] = standard_pose[i];
|
115 |
+
const [r, g, b] = joint_colors[i];
|
116 |
+
const color = (r << 16) | (g << 8) | (b << 0);
|
117 |
+
const geom = new THREE.SphereGeometry(JOINT_RADIUS, 32, 32);
|
118 |
+
const mat = new THREE.MeshBasicMaterial({ color: color });
|
119 |
+
const joint = new THREE.Mesh(geom, mat);
|
120 |
+
joint.name = joint_names[i];
|
121 |
+
joint.position.x = x * unit + x0;
|
122 |
+
joint.position.y = y * unit + y0;
|
123 |
+
joint.position.z = z + z0;
|
124 |
+
joint.dirty = true; // update limbs in next frame
|
125 |
+
joints.push(joint);
|
126 |
+
}
|
127 |
+
|
128 |
+
for (let i = 0; i < limb_pairs.length; ++i) {
|
129 |
+
const [r, g, b] = joint_colors[i];
|
130 |
+
const color = (r << 16) | (g << 8) | (b << 0);
|
131 |
+
const line = new MeshLine();
|
132 |
+
const mat = new MeshLineMaterial({ color: color, opacity: 0.6, transparent: true });
|
133 |
+
limbs.push(new THREE.Mesh(line, mat));
|
134 |
+
}
|
135 |
+
|
136 |
+
return [joints, limbs];
|
137 |
+
}
|
138 |
+
|
139 |
+
function init_3d(ui) {
|
140 |
+
const
|
141 |
+
container = ui.container,
|
142 |
+
canvas = ui.canvas,
|
143 |
+
notation = ui.notation,
|
144 |
+
indicator1 = ui.indicator1,
|
145 |
+
indicator2 = ui.indicator2,
|
146 |
+
width = () => canvas.width,
|
147 |
+
height = () => canvas.height,
|
148 |
+
unit = () => Math.min(width(), height()),
|
149 |
+
unit_max = () => Math.max(width(), height());
|
150 |
+
|
151 |
+
canvas.addEventListener('contextmenu', e => {
|
152 |
+
e.preventDefault();
|
153 |
+
}, false);
|
154 |
+
|
155 |
+
const scene = new THREE.Scene();
|
156 |
+
const default_bg = () => new THREE.Color(0x000000);
|
157 |
+
scene.background = default_bg();
|
158 |
+
const camera = new THREE.OrthographicCamera(width() / -2, width() / 2, height() / 2, height() / -2, 1, width() * 4);
|
159 |
+
camera.fixed_roll = ui.fixed_roll ? !!ui.fixed_roll.checked : false;
|
160 |
+
camera.position.z = unit_max() * 2;
|
161 |
+
|
162 |
+
const renderer = new THREE.WebGLRenderer({
|
163 |
+
canvas: canvas,
|
164 |
+
antialias: true,
|
165 |
+
alpha: true,
|
166 |
+
preserveDrawingBuffer: true,
|
167 |
+
});
|
168 |
+
renderer.setSize(width(), height());
|
169 |
+
|
170 |
+
function set_bg(image_path, dont_dispose) {
|
171 |
+
const old_tex = scene.background;
|
172 |
+
if (image_path === null) {
|
173 |
+
scene.background = default_bg();
|
174 |
+
if (old_tex && old_tex.dispose && !dont_dispose) old_tex.dispose();
|
175 |
+
return;
|
176 |
+
}
|
177 |
+
const tex = (image_path.isTexture || image_path.isColor) ? image_path : new THREE.TextureLoader().load(image_path);
|
178 |
+
scene.background = tex;
|
179 |
+
if (old_tex && old_tex.dispose && !dont_dispose) old_tex.dispose();
|
180 |
+
}
|
181 |
+
|
182 |
+
const bodies = new Map();
|
183 |
+
let selected_body = null;
|
184 |
+
let touched_body = null;
|
185 |
+
const touchable_objects = [];
|
186 |
+
const touchable_bodies = [];
|
187 |
+
const object_to_body = new Map();
|
188 |
+
|
189 |
+
function remove(mesh) {
|
190 |
+
if (mesh instanceof Array) {
|
191 |
+
for (let m of mesh) remove(m);
|
192 |
+
} else {
|
193 |
+
mesh.material.dispose();
|
194 |
+
mesh.geometry.dispose();
|
195 |
+
object_to_body.delete(mesh);
|
196 |
+
}
|
197 |
+
};
|
198 |
+
const add_body = (name, x0, y0, z0) => {
|
199 |
+
remove_body(name);
|
200 |
+
const [joints, limbs] = create_body(unit(), x0, y0, z0);
|
201 |
+
const group = new THREE.Group(); // for DragControls
|
202 |
+
|
203 |
+
const dispose = () => {
|
204 |
+
for (let joint of joints) {
|
205 |
+
array_remove(touchable_objects, joint);
|
206 |
+
remove(joint);
|
207 |
+
}
|
208 |
+
for (let limb of limbs) {
|
209 |
+
remove(limb);
|
210 |
+
scene.remove(limb);
|
211 |
+
}
|
212 |
+
array_remove(touchable_bodies, group);
|
213 |
+
scene.remove(group);
|
214 |
+
};
|
215 |
+
|
216 |
+
const reset = (dx, dy, dz) => {
|
217 |
+
if (dx === undefined) dx = x0;
|
218 |
+
if (dy === undefined) dy = y0;
|
219 |
+
if (dz === undefined) dz = z0;
|
220 |
+
for (let i = 0; i < standard_pose.length; ++i) {
|
221 |
+
const [x, y, z] = standard_pose[i];
|
222 |
+
joints[i].position.set(x * unit() + dx, y * unit() + dy, z + dz);
|
223 |
+
joints[i].dirty = true;
|
224 |
+
}
|
225 |
+
group.position.set(0, 0, 0);
|
226 |
+
body.dirty = true;
|
227 |
+
};
|
228 |
+
|
229 |
+
const body = {
|
230 |
+
name,
|
231 |
+
group,
|
232 |
+
joints,
|
233 |
+
limbs,
|
234 |
+
x0, y0, z0,
|
235 |
+
dispose,
|
236 |
+
reset,
|
237 |
+
dirty: true, // update limbs in next frame
|
238 |
+
};
|
239 |
+
|
240 |
+
for (let joint of joints) {
|
241 |
+
touchable_objects.push(joint);
|
242 |
+
object_to_body.set(joint, body);
|
243 |
+
group.add(joint);
|
244 |
+
}
|
245 |
+
for (let limb of limbs) {
|
246 |
+
scene.add(limb);
|
247 |
+
object_to_body.set(limb, body);
|
248 |
+
}
|
249 |
+
object_to_body.set(group, body);
|
250 |
+
|
251 |
+
bodies.set(name, body);
|
252 |
+
scene.add(group);
|
253 |
+
touchable_bodies.push(group);
|
254 |
+
|
255 |
+
return body;
|
256 |
+
};
|
257 |
+
|
258 |
+
const remove_body = name => {
|
259 |
+
if (!bodies.get(name)) return;
|
260 |
+
bodies.get(name).dispose();
|
261 |
+
bodies.delete(name);
|
262 |
+
};
|
263 |
+
|
264 |
+
const get_body_rect = body => {
|
265 |
+
const v = new THREE.Vector3();
|
266 |
+
let xmin = Infinity, xmax = -Infinity, ymin = Infinity, ymax = -Infinity;
|
267 |
+
for (let joint of body.joints) {
|
268 |
+
const wpos = joint.getWorldPosition(v);
|
269 |
+
const spos = wpos.project(camera);
|
270 |
+
if (spos.x < xmin) xmin = spos.x;
|
271 |
+
if (xmax < spos.x) xmax = spos.x;
|
272 |
+
if (spos.y < ymin) ymin = spos.y;
|
273 |
+
if (ymax < spos.y) ymax = spos.y;
|
274 |
+
}
|
275 |
+
return [xmin, ymin, xmax, ymax];
|
276 |
+
};
|
277 |
+
|
278 |
+
const default_body = add_body('defualt', 0, 0, 0);
|
279 |
+
|
280 |
+
const controls = new TrackballControls(camera, renderer.domElement);
|
281 |
+
const dragger_joint = new DragControls(touchable_objects, camera, renderer.domElement);
|
282 |
+
const dragger_body = new DragControls([], camera, renderer.domElement);
|
283 |
+
dragger_body.transformGroup = true;
|
284 |
+
|
285 |
+
dragger_joint.addEventListener('dragstart', () => { controls.enabled = false; });
|
286 |
+
dragger_joint.addEventListener('dragend', () => { controls.enabled = true; });
|
287 |
+
dragger_joint.addEventListener('drag', e => {
|
288 |
+
e.object.dirty = true;
|
289 |
+
object_to_body.get(e.object).dirty = true;
|
290 |
+
});
|
291 |
+
|
292 |
+
dragger_body.addEventListener('dragstart', () => { controls.enabled = false; });
|
293 |
+
dragger_body.addEventListener('dragend', () => { controls.enabled = true; });
|
294 |
+
dragger_body.addEventListener('drag', e => {
|
295 |
+
const body = object_to_body.get(e.object);
|
296 |
+
body.dirty = true;
|
297 |
+
for (let i = 0; i < body.joints.length; ++i) {
|
298 |
+
body.joints[i].dirty = true;
|
299 |
+
}
|
300 |
+
});
|
301 |
+
|
302 |
+
renderer.domElement.addEventListener('pointerdown', e => {
|
303 |
+
dragger_joint.enabled = e.button === 0;
|
304 |
+
dragger_body.enabled = e.button === 2;
|
305 |
+
}, true);
|
306 |
+
|
307 |
+
const rc = new THREE.Raycaster();
|
308 |
+
const m = new THREE.Vector2();
|
309 |
+
renderer.domElement.addEventListener('pointermove', e => {
|
310 |
+
e.preventDefault();
|
311 |
+
m.x = (e.offsetX / width()) * 2 - 1;
|
312 |
+
m.y = (1 - e.offsetY / height()) * 2 - 1;
|
313 |
+
rc.setFromCamera(m, camera);
|
314 |
+
const touched = rc.intersectObjects(touchable_objects);
|
315 |
+
|
316 |
+
// show label
|
317 |
+
if (touched.length != 0) {
|
318 |
+
const [dx, dy] = get_relative_offset(renderer.domElement, container);
|
319 |
+
notation.textContent = touched[0].object.name;
|
320 |
+
notation.style.left = `${e.offsetX + dx}px`;
|
321 |
+
notation.style.top = `${e.offsetY + dy - 32}px`;
|
322 |
+
notation.style.display = 'block';
|
323 |
+
} else {
|
324 |
+
notation.textContent = '';
|
325 |
+
notation.style.display = 'none';
|
326 |
+
}
|
327 |
+
|
328 |
+
// show temporary selection
|
329 |
+
if (touched.length != 0) {
|
330 |
+
touched_body = object_to_body.get(touched[0].object);
|
331 |
+
} else {
|
332 |
+
touched_body = null;
|
333 |
+
}
|
334 |
+
}, false);
|
335 |
+
|
336 |
+
renderer.domElement.addEventListener('pointerdown', e => {
|
337 |
+
e.preventDefault();
|
338 |
+
m.x = (e.offsetX / width()) * 2 - 1;
|
339 |
+
m.y = (1 - e.offsetY / height()) * 2 - 1;
|
340 |
+
rc.setFromCamera(m, camera);
|
341 |
+
const touched = rc.intersectObjects(touchable_objects);
|
342 |
+
|
343 |
+
// show selection
|
344 |
+
if (touched.length != 0) {
|
345 |
+
selected_body = object_to_body.get(touched[0].object);
|
346 |
+
const objs = dragger_body.getObjects();
|
347 |
+
objs.length = 0;
|
348 |
+
objs.push(selected_body.group);
|
349 |
+
dragger_body.onPointerDown(e);
|
350 |
+
} else {
|
351 |
+
selected_body = null;
|
352 |
+
dragger_body.getObjects().length = 0;
|
353 |
+
}
|
354 |
+
}, false);
|
355 |
+
|
356 |
+
if (ui.all_reset)
|
357 |
+
ui.all_reset.addEventListener('click', () => {
|
358 |
+
touched_body = null;
|
359 |
+
selected_body = null;
|
360 |
+
camera.position.set(0, 0, unit_max() * 2);
|
361 |
+
camera.rotation.set(0, 0, 0);
|
362 |
+
controls.reset();
|
363 |
+
for (let name of Array.from(bodies.keys()).slice(1)) {
|
364 |
+
remove_body(name);
|
365 |
+
}
|
366 |
+
for (let body of bodies.values()) {
|
367 |
+
body.reset(0, 0, 0);
|
368 |
+
}
|
369 |
+
}, false);
|
370 |
+
|
371 |
+
if (ui.reset_camera)
|
372 |
+
ui.reset_camera.addEventListener('click', () => {
|
373 |
+
camera.position.set(0, 0, unit_max() * 2);
|
374 |
+
camera.rotation.set(0, 0, 0);
|
375 |
+
controls.reset();
|
376 |
+
}, false);
|
377 |
+
|
378 |
+
if (ui.reset_pose)
|
379 |
+
ui.reset_pose.addEventListener('click', () => {
|
380 |
+
if (selected_body) {
|
381 |
+
selected_body.reset();
|
382 |
+
} else {
|
383 |
+
for (let [name, body] of bodies) {
|
384 |
+
body.reset();
|
385 |
+
}
|
386 |
+
}
|
387 |
+
}, false);
|
388 |
+
|
389 |
+
if (ui.fixed_roll)
|
390 |
+
ui.fixed_roll.addEventListener('change', () => {
|
391 |
+
camera.fixed_roll = !!ui.fixed_roll.checked;
|
392 |
+
}, false);
|
393 |
+
|
394 |
+
let body_num = 1;
|
395 |
+
if (ui.add_body)
|
396 |
+
ui.add_body.addEventListener('click', () => {
|
397 |
+
const last_body = selected_body ?? Array.from(bodies.values()).at(-1);
|
398 |
+
const base = last_body.joints[0].getWorldPosition(new THREE.Vector3());
|
399 |
+
const
|
400 |
+
dx = base.x - standard_pose[0][0] * unit(),
|
401 |
+
dy = base.y - standard_pose[0][1] * unit(),
|
402 |
+
dz = base.z - standard_pose[0][2];
|
403 |
+
add_body(`body_${body_num++}`, dx + 32, dy, dz);
|
404 |
+
}, false);
|
405 |
+
|
406 |
+
if (ui.remove_body)
|
407 |
+
ui.remove_body.addEventListener('click', () => {
|
408 |
+
if (!selected_body) {
|
409 |
+
ui.notify('No body is selected.', 'error');
|
410 |
+
return;
|
411 |
+
}
|
412 |
+
if (bodies.size <= 1) {
|
413 |
+
ui.notify('No body is not allowed.', 'error');
|
414 |
+
return;
|
415 |
+
}
|
416 |
+
remove_body(selected_body.name);
|
417 |
+
touched_body = null;
|
418 |
+
selected_body = null;
|
419 |
+
}, false);
|
420 |
+
|
421 |
+
const get_client_boundary = body => {
|
422 |
+
let [xmin, ymin, xmax, ymax] = get_body_rect(body);
|
423 |
+
|
424 |
+
// [-1,1] -> [0,width]
|
425 |
+
xmin = (xmin + 1) * width() / 2;
|
426 |
+
xmax = (xmax + 1) * width() / 2;
|
427 |
+
ymin = height() - (ymin + 1) * height() / 2;
|
428 |
+
ymax = height() - (ymax + 1) * height() / 2;
|
429 |
+
[ymin, ymax] = [ymax, ymin];
|
430 |
+
|
431 |
+
// add margin
|
432 |
+
xmin = xmin - 5 + renderer.domElement.offsetLeft;
|
433 |
+
xmax = xmax + 5 + renderer.domElement.offsetLeft;
|
434 |
+
ymin = ymin - 5 + renderer.domElement.offsetTop;
|
435 |
+
ymax = ymax + 5 + renderer.domElement.offsetTop;
|
436 |
+
|
437 |
+
return [xmin, ymin, xmax, ymax];
|
438 |
+
}
|
439 |
+
|
440 |
+
const size_change = (w, h) => {
|
441 |
+
if (w < 64 || h < 64) return;
|
442 |
+
canvas.width = w;
|
443 |
+
canvas.height = h;
|
444 |
+
renderer.setSize(w, h);
|
445 |
+
// update camera
|
446 |
+
camera.left = w / -2;
|
447 |
+
camera.right = w / 2;
|
448 |
+
camera.top = h / 2;
|
449 |
+
camera.bottom = h / -2;
|
450 |
+
camera.near = 1;
|
451 |
+
camera.far = w * 4;
|
452 |
+
camera.position.z = unit_max() * 2;
|
453 |
+
camera.updateProjectionMatrix();
|
454 |
+
controls.handleResize();
|
455 |
+
};
|
456 |
+
|
457 |
+
const width_input = ui.canvas_width, height_input = ui.canvas_height;
|
458 |
+
if (width_input && height_input) {
|
459 |
+
width_input.addEventListener('change', () => {
|
460 |
+
const w = +width_input.value;
|
461 |
+
const h = +height_input.value;
|
462 |
+
size_change(w, h);
|
463 |
+
}, false);
|
464 |
+
height_input.addEventListener('change', () => {
|
465 |
+
const w = +width_input.value;
|
466 |
+
const h = +height_input.value;
|
467 |
+
size_change(w, h);
|
468 |
+
}, false);
|
469 |
+
}
|
470 |
+
|
471 |
+
if (ui.bg)
|
472 |
+
ui.bg.addEventListener('change', e => {
|
473 |
+
const files = ui.bg.files;
|
474 |
+
if (files.length != 0) {
|
475 |
+
const file = files[0];
|
476 |
+
const r = new FileReader();
|
477 |
+
r.onload = () => set_bg(r.result);
|
478 |
+
r.readAsDataURL(file);
|
479 |
+
}
|
480 |
+
ui.bg.value = '';
|
481 |
+
}, false);
|
482 |
+
|
483 |
+
if (ui.reset_bg)
|
484 |
+
ui.reset_bg.addEventListener('click', () => set_bg(null), false);
|
485 |
+
|
486 |
+
function get_pose_dict(obj3d) {
|
487 |
+
return {
|
488 |
+
position: obj3d.position.toArray(),
|
489 |
+
rotation: obj3d.rotation.toArray(),
|
490 |
+
scale: obj3d.scale.toArray(),
|
491 |
+
up: obj3d.up.toArray(),
|
492 |
+
};
|
493 |
+
}
|
494 |
+
|
495 |
+
function set_pose_dict(obj3d, dict) {
|
496 |
+
obj3d.position.set(...dict.position);
|
497 |
+
obj3d.rotation.set(...dict.rotation);
|
498 |
+
obj3d.scale.set(...dict.scale);
|
499 |
+
obj3d.up.set(...dict.up);
|
500 |
+
}
|
501 |
+
|
502 |
+
if (ui.save_pose && ui.save_pose_callback)
|
503 |
+
ui.save_pose.addEventListener('click', async () => {
|
504 |
+
const name = prompt('Input pose name.');
|
505 |
+
if (name === undefined || name === null || name === '') return;
|
506 |
+
|
507 |
+
const screen = {
|
508 |
+
width: width(),
|
509 |
+
height: height(),
|
510 |
+
}
|
511 |
+
|
512 |
+
const camera_ = get_pose_dict(camera);
|
513 |
+
camera_.zoom = camera.zoom;
|
514 |
+
|
515 |
+
const joints = [];
|
516 |
+
for (let [name, body] of bodies) {
|
517 |
+
joints.push({
|
518 |
+
name,
|
519 |
+
joints: body.joints.map(j => get_pose_dict(j)),
|
520 |
+
group: get_pose_dict(body.group),
|
521 |
+
x0: body.x0,
|
522 |
+
y0: body.y0,
|
523 |
+
z0: body.z0,
|
524 |
+
});
|
525 |
+
}
|
526 |
+
|
527 |
+
const image = await ui.getDataURL();
|
528 |
+
|
529 |
+
const data = { name, image, screen, camera: camera_, joints };
|
530 |
+
const result = await ui.save_pose_callback(data);
|
531 |
+
ui.notify(result.result, result.ok ? 'success' : 'error');
|
532 |
+
}, false);
|
533 |
+
|
534 |
+
const onAnimateEndOneshot = [];
|
535 |
+
|
536 |
+
// joint and limb update
|
537 |
+
let elliptic_limbs = ui.elliptic_limbs ? !!ui.elliptic_limbs.checked : true;
|
538 |
+
//let joint_size_m = ui.joint_radius ? +ui.joint_radius.value / JOINT_RADIUS : 1.0;
|
539 |
+
let limb_size_m = ui.limb_width ? +ui.limb_width.value / LIMB_SIZE : 1.0;
|
540 |
+
if (ui.elliptic_limbs)
|
541 |
+
ui.elliptic_limbs.addEventListener('change', () => {
|
542 |
+
const b = !!ui.elliptic_limbs.checked;
|
543 |
+
if (elliptic_limbs !== b) {
|
544 |
+
elliptic_limbs = b;
|
545 |
+
for (let body of bodies.values()) {
|
546 |
+
body.dirty = true;
|
547 |
+
for (let i = 0; i < body.joints.length; ++i) {
|
548 |
+
body.joints[i].dirty = true;
|
549 |
+
}
|
550 |
+
}
|
551 |
+
}
|
552 |
+
}, false);
|
553 |
+
|
554 |
+
//if (ui.joint_radius)
|
555 |
+
// ui.joint_radius.addEventListener('input', () => {
|
556 |
+
// const new_val = +ui.joint_radius.value / JOINT_RADIUS;
|
557 |
+
// if (joint_size_m !== new_val) {
|
558 |
+
// joint_size_m = new_val;
|
559 |
+
// for (let body of bodies.values()) {
|
560 |
+
// body.dirty = true;
|
561 |
+
// for (let i = 0; i < body.joints.length; ++i) {
|
562 |
+
// body.joints[i].dirty = true;
|
563 |
+
// }
|
564 |
+
// }
|
565 |
+
// }
|
566 |
+
// }, false);
|
567 |
+
|
568 |
+
if (ui.limb_width)
|
569 |
+
ui.limb_width.addEventListener('input', () => {
|
570 |
+
const new_val = +ui.limb_width.value / LIMB_SIZE;
|
571 |
+
if (limb_size_m !== new_val) {
|
572 |
+
limb_size_m = new_val;
|
573 |
+
for (let body of bodies.values()) {
|
574 |
+
body.dirty = true;
|
575 |
+
for (let i = 0; i < body.joints.length; ++i) {
|
576 |
+
body.joints[i].dirty = true;
|
577 |
+
}
|
578 |
+
}
|
579 |
+
}
|
580 |
+
}, false);
|
581 |
+
|
582 |
+
const limb_vecs = Array.from(Array(LIMB_N)).map(x => new THREE.Vector3());
|
583 |
+
function elliptic_limb_width(p) {
|
584 |
+
// draw limb ellipse
|
585 |
+
// x^2 / a^2 + y^2 / b^2 = 1
|
586 |
+
// a := half of distance between two joints
|
587 |
+
// b := 2 * LIMB_SIZE / camera.zoom
|
588 |
+
// {a(2p-1)}^2 / a^2 + y^2 / b^2 = 1
|
589 |
+
// y^2 = b^2 { 1 - (2p-1)^2 }
|
590 |
+
const b = 2 * LIMB_SIZE * limb_size_m / camera.zoom;
|
591 |
+
const pp = 2 * p - 1;
|
592 |
+
return b * Math.sqrt(1 - pp * pp);
|
593 |
+
}
|
594 |
+
function stick_limb_width(p) {
|
595 |
+
// half width of ellipse
|
596 |
+
return LIMB_SIZE * limb_size_m / camera.zoom;
|
597 |
+
}
|
598 |
+
function create_limb(mesh, from, to) {
|
599 |
+
const s0 = limb_vecs[0];
|
600 |
+
const s1 = limb_vecs[LIMB_N - 1];
|
601 |
+
from.getWorldPosition(s0);
|
602 |
+
to.getWorldPosition(s1);
|
603 |
+
const N = LIMB_N - 1;
|
604 |
+
for (let i = 1; i < limb_vecs.length - 1; ++i) {
|
605 |
+
limb_vecs[i].lerpVectors(s0, s1, i / N);
|
606 |
+
}
|
607 |
+
mesh.geometry.setPoints(limb_vecs, elliptic_limbs ? elliptic_limb_width : stick_limb_width);
|
608 |
+
}
|
609 |
+
|
610 |
+
let low_fps = ui.low_fps ? !!ui.low_fps.checked : false;
|
611 |
+
if (ui.low_fps)
|
612 |
+
ui.low_fps.addEventListener('change', () => {
|
613 |
+
low_fps = !!ui.low_fps.checked;
|
614 |
+
}, false);
|
615 |
+
|
616 |
+
let last_zoom = camera.zoom;
|
617 |
+
let running = true;
|
618 |
+
//const frames = [0,0,0,0,0,0,0,0,0,0], frame_index = 0;
|
619 |
+
let last_tick = globalThis.performance.now();
|
620 |
+
const animate = () => {
|
621 |
+
const t0 = globalThis.performance.now();
|
622 |
+
//frames[(frame_index++)%frames.length] = t0 - last_tick;
|
623 |
+
//last_tick = t0;
|
624 |
+
//console.log(frames.reduce((acc, cur) => acc + cur) / frames.length);
|
625 |
+
|
626 |
+
requestAnimationFrame(animate);
|
627 |
+
if (!running) return;
|
628 |
+
|
629 |
+
if (controls.enabled) {
|
630 |
+
if (controls.screen.width === 0 && controls.screen.height === 0) {
|
631 |
+
controls.handleResize();
|
632 |
+
}
|
633 |
+
}
|
634 |
+
controls.update();
|
635 |
+
|
636 |
+
if (low_fps && t0 - last_tick < 30) return; // nearly 30fps
|
637 |
+
last_tick = t0;
|
638 |
+
|
639 |
+
for (let [name, body] of bodies) {
|
640 |
+
const { joints, limbs, group } = body;
|
641 |
+
|
642 |
+
// update joint size
|
643 |
+
for (let joint of joints) {
|
644 |
+
joint.scale.setScalar(1 / camera.zoom);
|
645 |
+
}
|
646 |
+
|
647 |
+
// show limbs
|
648 |
+
const zoom_changed = last_zoom !== camera.zoom;
|
649 |
+
if (body.dirty || zoom_changed) {
|
650 |
+
for (let i = 0; i < limb_pairs.length; ++i) {
|
651 |
+
const [from_index, to_index] = limb_pairs[i];
|
652 |
+
const [from, to] = [joints[from_index], joints[to_index]];
|
653 |
+
if (from.dirty || to.dirty || zoom_changed) {
|
654 |
+
create_limb(limbs[i], from, to);
|
655 |
+
}
|
656 |
+
}
|
657 |
+
|
658 |
+
for (let i = 0; i < joints.length; ++i) {
|
659 |
+
joints[i].dirty = false;
|
660 |
+
}
|
661 |
+
body.dirty = false;
|
662 |
+
}
|
663 |
+
}
|
664 |
+
|
665 |
+
last_zoom = camera.zoom;
|
666 |
+
|
667 |
+
// show selection
|
668 |
+
if (touched_body) {
|
669 |
+
let [xmin, ymin, xmax, ymax] = get_client_boundary(touched_body);
|
670 |
+
const st = indicator2.style;
|
671 |
+
st.display = 'block';
|
672 |
+
st.left = `${xmin}px`;
|
673 |
+
st.top = `${ymin}px`;
|
674 |
+
st.width = `${xmax - xmin}px`;
|
675 |
+
st.height = `${ymax - ymin}px`;
|
676 |
+
} else {
|
677 |
+
indicator2.style.display = 'none';
|
678 |
+
}
|
679 |
+
|
680 |
+
if (selected_body) {
|
681 |
+
let [xmin, ymin, xmax, ymax] = get_client_boundary(selected_body);
|
682 |
+
const st = indicator1.style;
|
683 |
+
st.display = 'block';
|
684 |
+
st.left = `${xmin}px`;
|
685 |
+
st.top = `${ymin}px`;
|
686 |
+
st.width = `${xmax - xmin}px`;
|
687 |
+
st.height = `${ymax - ymin}px`;
|
688 |
+
} else {
|
689 |
+
indicator1.style.display = 'none';
|
690 |
+
}
|
691 |
+
|
692 |
+
if (camera.fixed_roll) camera.up.set(0, 1, 0);
|
693 |
+
renderer.render(scene, camera);
|
694 |
+
|
695 |
+
for (let fn of onAnimateEndOneshot) {
|
696 |
+
fn();
|
697 |
+
}
|
698 |
+
onAnimateEndOneshot.length = 0;
|
699 |
+
};
|
700 |
+
|
701 |
+
ui.loadPose = function (data) {
|
702 |
+
selected_body = null;
|
703 |
+
touched_body = null;
|
704 |
+
touchable_objects.length = 0;
|
705 |
+
touchable_bodies.length = 0;
|
706 |
+
object_to_body.clear();
|
707 |
+
for (let name of bodies.keys()) {
|
708 |
+
remove_body(name);
|
709 |
+
}
|
710 |
+
|
711 |
+
// screen
|
712 |
+
size_change(data.screen.width, data.screen.height);
|
713 |
+
if (width_input) width_input.value = data.screen.width;
|
714 |
+
if (height_input) height_input.value = data.screen.height;
|
715 |
+
|
716 |
+
// camera
|
717 |
+
set_pose_dict(camera, data.camera);
|
718 |
+
camera.zoom = data.camera.zoom;
|
719 |
+
camera.updateProjectionMatrix();
|
720 |
+
|
721 |
+
// bodies
|
722 |
+
|
723 |
+
// update `body_num`
|
724 |
+
const body_names = data.joints.map(x => {
|
725 |
+
const m = /^body_(\d+)$/.exec(x.name);
|
726 |
+
return m ? +m[1] : -1;
|
727 |
+
}).filter(x => 0 <= x);
|
728 |
+
if (body_names.length == 0) {
|
729 |
+
body_num = 0;
|
730 |
+
} else {
|
731 |
+
body_num = Math.max(...body_names) + 1;
|
732 |
+
}
|
733 |
+
|
734 |
+
for (let dict of data.joints) {
|
735 |
+
const body = add_body(dict.name, dict.x0, dict.y0, dict.z0);
|
736 |
+
for (let i = 0, e = Math.min(body.joints.length, dict.joints.length); i < e; ++i) {
|
737 |
+
set_pose_dict(body.joints[i], dict.joints[i]);
|
738 |
+
}
|
739 |
+
set_pose_dict(body.group, dict.group);
|
740 |
+
}
|
741 |
+
};
|
742 |
+
|
743 |
+
ui.getDataURL = async function () {
|
744 |
+
const pr = new Promise(resolve => {
|
745 |
+
const current_bg = scene.background;
|
746 |
+
set_bg(null, true);
|
747 |
+
onAnimateEndOneshot.push(() => {
|
748 |
+
resolve(renderer.domElement.toDataURL('image/png'));
|
749 |
+
set_bg(current_bg);
|
750 |
+
});
|
751 |
+
});
|
752 |
+
return await pr;
|
753 |
+
};
|
754 |
+
|
755 |
+
ui.getBlob = async function () {
|
756 |
+
const pr = new Promise(resolve => {
|
757 |
+
const current_bg = scene.background;
|
758 |
+
set_bg(null, true);
|
759 |
+
onAnimateEndOneshot.push(() => {
|
760 |
+
renderer.domElement.toBlob(blob => {
|
761 |
+
resolve(blob);
|
762 |
+
set_bg(current_bg);
|
763 |
+
});
|
764 |
+
});
|
765 |
+
});
|
766 |
+
return await pr;
|
767 |
+
};
|
768 |
+
|
769 |
+
ui.stop = function () {
|
770 |
+
running = false;
|
771 |
+
dragger_joint.deactivate();
|
772 |
+
dragger_joint.enabled = false;
|
773 |
+
dragger_body.deactivate();
|
774 |
+
dragger_body.enabled = false;
|
775 |
+
controls.enabled = false;
|
776 |
+
};
|
777 |
+
|
778 |
+
ui.play = function () {
|
779 |
+
running = true;
|
780 |
+
dragger_joint.activate();
|
781 |
+
dragger_joint.enabled = true;
|
782 |
+
dragger_body.activate();
|
783 |
+
dragger_body.enabled = true;
|
784 |
+
controls.enabled = true;
|
785 |
+
controls.handleResize();
|
786 |
+
};
|
787 |
+
|
788 |
+
return animate;
|
789 |
+
}
|
790 |
+
|
791 |
+
function init(ui) {
|
792 |
+
if (ui.save)
|
793 |
+
ui.save.addEventListener('click', async () => {
|
794 |
+
const a = document.createElement('a');
|
795 |
+
if (ui.getDataURL) {
|
796 |
+
a.href = await ui.getDataURL('image/png');
|
797 |
+
} else {
|
798 |
+
a.href = ui.canvas.toDataURL('image/png');
|
799 |
+
}
|
800 |
+
a.download = 'download.png';
|
801 |
+
a.click();
|
802 |
+
ui.notify('save success');
|
803 |
+
}, false);
|
804 |
+
|
805 |
+
if (ui.copy)
|
806 |
+
ui.copy.addEventListener('click', async () => {
|
807 |
+
if (globalThis.ClipboardItem === undefined) {
|
808 |
+
alert('`ClipboardItem` is not defined. If you are in Firefox, change about:config -> dom.events.asyncClipboard.clipboardItem to `true`.')
|
809 |
+
return;
|
810 |
+
}
|
811 |
+
|
812 |
+
async function get_blob() {
|
813 |
+
if (ui.getBlob) {
|
814 |
+
return await ui.getBlob();
|
815 |
+
} else {
|
816 |
+
return await new Promise(resolve => ui.canvas.toBlob(blob => resolve(blob)));
|
817 |
+
}
|
818 |
+
}
|
819 |
+
try {
|
820 |
+
const blob = await get_blob();
|
821 |
+
const data = new ClipboardItem({ [blob.type]: blob });
|
822 |
+
navigator.clipboard.write([data]);
|
823 |
+
ui.notify('copy success');
|
824 |
+
} catch (e) {
|
825 |
+
ui.notify(`failed to copy data: ${e.message}`, 'error');
|
826 |
+
}
|
827 |
+
}, false);
|
828 |
+
}
|
829 |
+
|
830 |
+
function array_remove(array, item) {
|
831 |
+
let index = array.indexOf(item);
|
832 |
+
while (0 <= index) {
|
833 |
+
array.splice(index, 1);
|
834 |
+
index = array.indexOf(item);
|
835 |
+
}
|
836 |
+
}
|
837 |
+
|
838 |
+
function get_relative_offset(target, origin) {
|
839 |
+
const r0 = origin.getBoundingClientRect();
|
840 |
+
const r1 = target.getBoundingClientRect();
|
841 |
+
return [r1.left - r0.left, r1.top - r0.top];
|
842 |
+
}
|
843 |
+
|
844 |
+
export { init, init_3d };
|
posex/js/three.module.js
ADDED
The diff for this file is too large to render.
See raw diff
|
|
posex/requirements.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
flask
|
2 |
+
pillow
|
posex/saved_poses/bridge.png
ADDED
posex/saved_poses/sample.png
ADDED
posex/scripts/__pycache__/posex.cpython-310.pyc
ADDED
Binary file (6.78 kB). View file
|
|
posex/scripts/posex.py
ADDED
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import io
|
3 |
+
import base64
|
4 |
+
import json
|
5 |
+
from typing import Callable, Any
|
6 |
+
from PIL import Image
|
7 |
+
import gradio as gr
|
8 |
+
from modules import scripts
|
9 |
+
from modules.processing import StableDiffusionProcessing
|
10 |
+
from modules.shared import opts
|
11 |
+
from modules import extensions
|
12 |
+
|
13 |
+
from common import posex_utils as posex
|
14 |
+
|
15 |
+
if '__file__' in globals():
|
16 |
+
posex.set_save_dir(os.path.join(os.path.dirname(__file__), '..', 'saved_poses'))
|
17 |
+
else:
|
18 |
+
# cf. https://stackoverflow.com/a/53293924
|
19 |
+
import inspect
|
20 |
+
posex.set_save_dir(os.path.join(os.path.dirname(inspect.getfile(lambda: None)), '..', 'saved_poses'))
|
21 |
+
|
22 |
+
class Script(scripts.Script):
|
23 |
+
|
24 |
+
def title(self):
|
25 |
+
return 'Posex'
|
26 |
+
|
27 |
+
def show(self, is_img2img):
|
28 |
+
return scripts.AlwaysVisible
|
29 |
+
|
30 |
+
def ui(self, is_img2img):
|
31 |
+
id = lambda s: f'posex-{["t2i", "i2i"][is_img2img]}-{s}'
|
32 |
+
js = lambda s: f'globalThis["{id(s)}"]'
|
33 |
+
|
34 |
+
ext = get_self_extension()
|
35 |
+
if ext is None:
|
36 |
+
return []
|
37 |
+
js_ = [f'{x.path}?{os.path.getmtime(x.path)}' for x in ext.list_files('javascript/lazyload', '.js')]
|
38 |
+
js_.insert(0, ext.path)
|
39 |
+
|
40 |
+
with gr.Accordion('Posex', open=False, elem_id=id('accordion')):
|
41 |
+
with gr.Row():
|
42 |
+
enabled = gr.Checkbox(value=False, label='Send this image to ControlNet.', elem_id=id('enabled'))
|
43 |
+
cn_num = gr.Number(value=0, precision=0, label='Target ControlNet number', visible=self.max_cn_num()!=1)
|
44 |
+
|
45 |
+
gr.HTML(value='\n'.join(js_), elem_id=id('js'), visible=False)
|
46 |
+
|
47 |
+
gr.HTML(value='', elem_id=id('html'))
|
48 |
+
|
49 |
+
with gr.Group(visible=False):
|
50 |
+
sink = gr.HTML(value='', visible=False) # to suppress error in javascript
|
51 |
+
base64 = js2py('base64', id, js, sink)
|
52 |
+
py2js('allposes', all_pose, id, js, sink)
|
53 |
+
jscall('delpose', delete_pose, id, js, sink)
|
54 |
+
jscall('savepose', save_pose, id, js, sink)
|
55 |
+
jscall('loadpose', load_pose, id, js, sink)
|
56 |
+
|
57 |
+
return [enabled, base64, cn_num]
|
58 |
+
|
59 |
+
def process(self, p: StableDiffusionProcessing, enabled: bool = False, b64: str = '', cn_num: int = 0):
|
60 |
+
if not enabled or b64 is None or len(b64) == 0:
|
61 |
+
return
|
62 |
+
|
63 |
+
cn_num = int(cn_num)
|
64 |
+
max_cn_num = self.max_cn_num()
|
65 |
+
if max_cn_num != 1:
|
66 |
+
v = (cn_num + max_cn_num) % max_cn_num
|
67 |
+
if v < 0:
|
68 |
+
raise ValueError(f'[posex] invalid ControlNet number: {cn_num}')
|
69 |
+
cn_num = v
|
70 |
+
else:
|
71 |
+
cn_num = 0
|
72 |
+
|
73 |
+
binary = io.BytesIO(base64.b64decode(b64[len('data:image/png;base64,'):]))
|
74 |
+
image = Image.open(binary)
|
75 |
+
|
76 |
+
opts.control_net_allow_script_control = True
|
77 |
+
self.set_p_value(p, 'control_net_enabled', cn_num, True)
|
78 |
+
self.set_p_value(p, 'control_net_input_image', cn_num, image)
|
79 |
+
|
80 |
+
def postprocess(self, p, processed, enabled: bool = False, b64: str = '', cn_num: int = 0):
|
81 |
+
if not enabled or b64 is None or len(b64) == 0:
|
82 |
+
return
|
83 |
+
|
84 |
+
opts.control_net_allow_script_control = False
|
85 |
+
|
86 |
+
def set_p_value(self, p: StableDiffusionProcessing, attr: str, idx: int, v: Any):
|
87 |
+
value = getattr(p, attr, None)
|
88 |
+
if isinstance(value, list):
|
89 |
+
value[idx] = v
|
90 |
+
else:
|
91 |
+
# if value is None, ControlNet uses default value
|
92 |
+
value = [value] * self.max_cn_num()
|
93 |
+
value[idx] = v
|
94 |
+
setattr(p, attr, value)
|
95 |
+
|
96 |
+
def max_cn_num(self):
|
97 |
+
if opts.data is None:
|
98 |
+
return 1
|
99 |
+
return int(opts.data.get('control_net_max_models_num', 1))
|
100 |
+
|
101 |
+
|
102 |
+
def js2py(
|
103 |
+
name: str,
|
104 |
+
id: Callable[[str], str],
|
105 |
+
js: Callable[[str], str],
|
106 |
+
sink: gr.components.IOComponent,
|
107 |
+
) -> gr.Textbox:
|
108 |
+
|
109 |
+
v_set = gr.Button(elem_id=id(f'{name}_set'))
|
110 |
+
v = gr.Textbox(elem_id=id(name))
|
111 |
+
v_sink = gr.Textbox()
|
112 |
+
v_set.click(fn=None, _js=js(name), outputs=[v, v_sink])
|
113 |
+
v_sink.change(fn=None, _js=js(f'{name}_after'), outputs=[sink])
|
114 |
+
return v
|
115 |
+
|
116 |
+
def py2js(
|
117 |
+
name: str,
|
118 |
+
fn: Callable[[], str],
|
119 |
+
id: Callable[[str], str],
|
120 |
+
js: Callable[[str], str],
|
121 |
+
sink: gr.components.IOComponent,
|
122 |
+
) -> None:
|
123 |
+
|
124 |
+
v_fire = gr.Button(elem_id=id(f'{name}_get'))
|
125 |
+
v_sink = gr.Textbox()
|
126 |
+
v_sink2 = gr.Textbox()
|
127 |
+
v_fire.click(fn=wrap_api(fn), outputs=[v_sink, v_sink2])
|
128 |
+
v_sink2.change(fn=None, _js=js(name), inputs=[v_sink], outputs=[sink])
|
129 |
+
|
130 |
+
def jscall(
|
131 |
+
name: str,
|
132 |
+
fn: Callable[[str], str],
|
133 |
+
id: Callable[[str], str],
|
134 |
+
js: Callable[[str], str],
|
135 |
+
sink: gr.components.IOComponent,
|
136 |
+
) -> None:
|
137 |
+
|
138 |
+
v_args_set = gr.Button(elem_id=id(f'{name}_args_set'))
|
139 |
+
v_args = gr.Textbox(elem_id=id(f'{name}_args'))
|
140 |
+
v_args_sink = gr.Textbox()
|
141 |
+
v_args_set.click(fn=None, _js=js(f'{name}_args'), outputs=[v_args, v_args_sink])
|
142 |
+
v_args_sink.change(fn=None, _js=js(f'{name}_args_after'), outputs=[sink])
|
143 |
+
|
144 |
+
v_fire = gr.Button(elem_id=id(f'{name}_get'))
|
145 |
+
v_sink = gr.Textbox()
|
146 |
+
v_sink2 = gr.Textbox()
|
147 |
+
v_fire.click(fn=wrap_api(fn), inputs=[v_args], outputs=[v_sink, v_sink2])
|
148 |
+
v_sink2.change(fn=None, _js=js(name), inputs=[v_sink], outputs=[sink])
|
149 |
+
|
150 |
+
|
151 |
+
def get_self_extension():
|
152 |
+
if '__file__' in globals():
|
153 |
+
filepath = __file__
|
154 |
+
else:
|
155 |
+
import inspect
|
156 |
+
filepath = inspect.getfile(lambda: None)
|
157 |
+
for ext in extensions.active():
|
158 |
+
if ext.path in filepath:
|
159 |
+
return ext
|
160 |
+
|
161 |
+
# APIs
|
162 |
+
|
163 |
+
def wrap_api(fn):
|
164 |
+
_r = 0
|
165 |
+
def f(*args, **kwargs):
|
166 |
+
nonlocal _r
|
167 |
+
_r += 1
|
168 |
+
v = fn(*args, **kwargs)
|
169 |
+
return v, str(_r)
|
170 |
+
return f
|
171 |
+
|
172 |
+
def all_pose():
|
173 |
+
return json.dumps(list(posex.all_poses()))
|
174 |
+
|
175 |
+
def delete_pose(args):
|
176 |
+
posex.delete_pose(json.loads(args)[0])
|
177 |
+
return ''
|
178 |
+
|
179 |
+
def save_pose(args):
|
180 |
+
posex.save_pose(json.loads(args)[0])
|
181 |
+
return ''
|
182 |
+
|
183 |
+
def load_pose(args):
|
184 |
+
return json.dumps(posex.load_pose(json.loads(args)[0]))
|
posex/style.css
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
* CSS for WebUI
|
3 |
+
**/
|
4 |
+
|
5 |
+
.posex_cont {
|
6 |
+
display: flex;
|
7 |
+
flex-flow: column;
|
8 |
+
gap: 0.5em 0;
|
9 |
+
width: max-content;
|
10 |
+
overflow: auto !important;
|
11 |
+
max-width: 100%;
|
12 |
+
}
|
13 |
+
|
14 |
+
.posex_cont input[type=checkbox] {
|
15 |
+
appearance: auto;
|
16 |
+
}
|
17 |
+
|
18 |
+
.posex_reset_cont {
|
19 |
+
display: flex;
|
20 |
+
flex-direction: row;
|
21 |
+
gap: 0 0.5em;
|
22 |
+
}
|
23 |
+
|
24 |
+
.posex_box {
|
25 |
+
display: block;
|
26 |
+
border: 1px solid gray;
|
27 |
+
padding: 0.5em 0;
|
28 |
+
text-decoration: none;
|
29 |
+
text-align: center;
|
30 |
+
}
|
31 |
+
|
32 |
+
.posex_reset_cont .posex_box {
|
33 |
+
flex: 1 1 0;
|
34 |
+
}
|
35 |
+
|
36 |
+
.posex_canvas_cont {
|
37 |
+
display: flex;
|
38 |
+
flex-direction: row;
|
39 |
+
}
|
40 |
+
|
41 |
+
.posex_canvas_size {
|
42 |
+
background-color: transparent !important;
|
43 |
+
}
|
44 |
+
|
45 |
+
.posex_setting_cont {
|
46 |
+
margin-left: 1em;
|
47 |
+
display: flex;
|
48 |
+
flex-direction: column;
|
49 |
+
gap: 0.25em;
|
50 |
+
}
|
51 |
+
|
52 |
+
.posex_body {
|
53 |
+
width: max-content;
|
54 |
+
min-width: 100%;
|
55 |
+
text-align: left;
|
56 |
+
border: 1px solid gray;
|
57 |
+
padding: 0.5em;
|
58 |
+
}
|
59 |
+
|
60 |
+
.posex_bg_cont {
|
61 |
+
display: flex;
|
62 |
+
flex-direction: row;
|
63 |
+
gap: 0 0.25em;
|
64 |
+
}
|
65 |
+
|
66 |
+
.posex_bg {
|
67 |
+
flex: 0 0 0;
|
68 |
+
border: 1px solid gray;
|
69 |
+
padding: 0.25em 1em;
|
70 |
+
min-width: 5em;
|
71 |
+
}
|
72 |
+
|
73 |
+
.posex_notation {
|
74 |
+
display: none;
|
75 |
+
position: absolute;
|
76 |
+
color: black;
|
77 |
+
background-color: rgba(255, 255, 255, 0.75);
|
78 |
+
padding: 0.1em 0.25em;
|
79 |
+
pointer-events: none;
|
80 |
+
font-size: small;
|
81 |
+
}
|
82 |
+
|
83 |
+
.posex_indicator1 {
|
84 |
+
display: none;
|
85 |
+
position: absolute;
|
86 |
+
outline: 1px solid white;
|
87 |
+
pointer-events: none;
|
88 |
+
}
|
89 |
+
|
90 |
+
.posex_indicator2 {
|
91 |
+
display: none;
|
92 |
+
position: absolute;
|
93 |
+
outline: 1px solid gray;
|
94 |
+
pointer-events: none;
|
95 |
+
}
|
96 |
+
|
97 |
+
.posex_misc_cont {
|
98 |
+
display: flex;
|
99 |
+
flex-flow: row;
|
100 |
+
gap: 0 0.5em;
|
101 |
+
}
|
102 |
+
|
103 |
+
.posex_misc {
|
104 |
+
flex: 1 1 0;
|
105 |
+
}
|
106 |
+
|
107 |
+
.posex_saved_poses {
|
108 |
+
display: flex;
|
109 |
+
flex-direction: row;
|
110 |
+
gap: 0.25em;
|
111 |
+
font-size: small;
|
112 |
+
}
|
113 |
+
|
114 |
+
.posex_saved_poses > * {
|
115 |
+
margin: 0;
|
116 |
+
outline: 1px solid gray;
|
117 |
+
max-width: 128px;
|
118 |
+
}
|
119 |
+
|
120 |
+
.posex_saved_poses img {
|
121 |
+
max-width: 128px;
|
122 |
+
max-height: 128px;
|
123 |
+
}
|
124 |
+
|
125 |
+
.posex_saved_poses figcaption {
|
126 |
+
padding: 0 0.25em;
|
127 |
+
overflow-wrap: anywhere; /* Opera Android may not be able to interpret `anywhere` keyword. */
|
128 |
+
}
|
129 |
+
|
130 |
+
.posex_saved_poses .close {
|
131 |
+
position: absolute;
|
132 |
+
cursor: pointer;
|
133 |
+
background-color: white;
|
134 |
+
opacity: 0.5 !important;
|
135 |
+
width: 16px;
|
136 |
+
height: 16px;
|
137 |
+
text-align: center;
|
138 |
+
vertical-align: middle;
|
139 |
+
margin: 0;
|
140 |
+
padding: 0;
|
141 |
+
font-family: monospace;
|
142 |
+
}
|
143 |
+
|
144 |
+
.posex_saved_poses .close:hover {
|
145 |
+
opacity: 1.0 !important;
|
146 |
+
}
|
147 |
+
|
148 |
+
.posex_saved_poses .close2 {
|
149 |
+
display: none;
|
150 |
+
position: absolute;
|
151 |
+
left: 1.5em;
|
152 |
+
top: 0;
|
153 |
+
}
|
154 |
+
|
155 |
+
.posex_saved_poses .close:hover .close2 {
|
156 |
+
display: block;
|
157 |
+
color: white;
|
158 |
+
pointer-events: none;
|
159 |
+
}
|
put extensions here.txt
ADDED
File without changes
|
sd-webui-lora-block-weight/README.md
ADDED
@@ -0,0 +1,350 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# LoRA Block Weight
|
2 |
+
- custom script for [AUTOMATIC1111's stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui)
|
3 |
+
- When applying Lora, strength can be set block by block.
|
4 |
+
|
5 |
+
- [AUTOMATIC1111's stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui) 用のスクリプトです
|
6 |
+
- Loraを適用する際、強さを階層ごとに設定できます
|
7 |
+
|
8 |
+
### Updates/更新情報
|
9 |
+
2023.5.24.2000(JST)
|
10 |
+
- changed directory for presets(extentions/sd-webui-lora-block-weight/scripts/)
|
11 |
+
- プリセットの保存フォルダがextentions/sd-webui-lora-block-weight/scripts/に変更になりました。
|
12 |
+
|
13 |
+
2023.5.12.2100(JST)
|
14 |
+
- changed syntax of lycoris
|
15 |
+
- lycorisの書式を変更しました
|
16 |
+
|
17 |
+
2023.04.14.2000(JST)
|
18 |
+
- support LyCORIS(a1111-sd-webui-lycoris)
|
19 |
+
- LyCORIS(a1111-sd-webui-lycoris)に対応
|
20 |
+
|
21 |
+
2023.03.20.2030(JST)
|
22 |
+
- Comment lines can now be added to presets
|
23 |
+
- プリセットにコメント行を追加できるようになりました
|
24 |
+
- support XYZ plot hires.fix
|
25 |
+
- XYZプロットがhires.fixに対応しました
|
26 |
+
|
27 |
+
2023.03.16.2030(JST)
|
28 |
+
- [LyCORIS](https://github.com/KohakuBlueleaf/LyCORIS)に対応しました
|
29 |
+
- Support [LyCORIS](https://github.com/KohakuBlueleaf/LyCORIS)
|
30 |
+
|
31 |
+
別途[LyCORIS Extention](https://github.com/KohakuBlueleaf/a1111-sd-webui-locon)が必要です。
|
32 |
+
For use LyCORIS, [Extension](https://github.com/KohakuBlueleaf/a1111-sd-webui-locon) for LyCORIS needed.
|
33 |
+
|
34 |
+
日本語説明は[後半](#概要)後半にあります。
|
35 |
+
|
36 |
+
# Overview
|
37 |
+
Lora is a powerful tool, but it is sometimes difficult to use and can affect areas that you do not want it to affect. This script allows you to set the weights block-by-block. Using this script, you may be able to get the image you want.
|
38 |
+
|
39 |
+
## Usage
|
40 |
+
Place lora_block_weight.py in the script folder.
|
41 |
+
Or you can install from Extentions tab in web-ui. When installing, please restart web-ui.bat.
|
42 |
+
|
43 |
+
### Active
|
44 |
+
Check this box to activate it.
|
45 |
+
|
46 |
+
### Prompt
|
47 |
+
In the prompt box, enter the Lora you wish to use as usual. Enter the weight or identifier by typing ":" after the strength value. The identifier can be edited in the Weights setting.
|
48 |
+
```
|
49 |
+
<lora:"lora name":1:0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0>.
|
50 |
+
<lora:"lora name":1:0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>. (a1111-sd-webui-locon, etc.)
|
51 |
+
<lora:"lora name":1:IN02>
|
52 |
+
<lyco:"lora name":1:1:lbw=IN02> (a1111-sd-webui-lycoris)
|
53 |
+
<lyco:"lora name":1:1:lbw=1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0> (a1111-sd-webui-lycoris)
|
54 |
+
```
|
55 |
+
For LyCORIS using a1111-sd-webui-lycoris, syntax is different.
|
56 |
+
`lbw=IN02` is used and follow lycoirs syntax for others such as unet or else.
|
57 |
+
a1111-sd-webui-lycoris is under under development, so this syntax might be changed.
|
58 |
+
|
59 |
+
Lora strength is in effect and applies to the entire Blocks.
|
60 |
+
It is case-sensitive.
|
61 |
+
For LyCORIS, full-model blobks used,so you need to input 26 weights.
|
62 |
+
You can use weight for LoRA, in this case, the weight of blocks not in LoRA is set to 1.
|
63 |
+
If the above format is not used, the preset will treat it as a comment line.
|
64 |
+
|
65 |
+
### Weights Setting
|
66 |
+
Enter the identifier and weights.
|
67 |
+
Unlike the full model, Lora is divided into 17 blocks, including the encoder. Therefore, enter 17 values.
|
68 |
+
BASE, IN, OUT, etc. are the blocks equivalent to the full model.
|
69 |
+
|
70 |
+
|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|
|
71 |
+
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
|
72 |
+
|BASE|IN01|IN02|IN04|IN05|IN07|IN08|MID|OUT03|OUT04|OUT05|OUT06|OUT07|OUT08|OUT09|OUT10|OUT11|
|
73 |
+
|
74 |
+
LyCORIS, etc.
|
75 |
+
|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|
|
76 |
+
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
|
77 |
+
|BASE|IN00|IN01|IN02|IN03|IN04|IN05|IN06|IN07|IN08|IN09|IN10|IN11|MID|OUT00|OUT01|OUT02|OUT03|OUT04|OUT05|OUT06|OUT07|OUT08|OUT09|OUT10|OUT11|
|
78 |
+
|
79 |
+
### Special Values (Random)
|
80 |
+
Basically, a numerical value must be entered to work correctly, but by entering `R` and `U`, a random value will be entered.
|
81 |
+
R : Numerical value with 3 decimal places from 0~1
|
82 |
+
U : 3 decimal places from -1.5 to 1.5
|
83 |
+
|
84 |
+
For example, if ROUT:1,1,1,1,1,1,1,1,R,R,R,R,R,R,R,R,R
|
85 |
+
Only the OUT blocks is randomized.
|
86 |
+
The randomized values will be displayed on the command prompt screen when the image is generated.
|
87 |
+
|
88 |
+
### Special Values (Dynamic)
|
89 |
+
The special value `X` may also be included to use a dynamic weight specified in the LoRA syntax. This is activated by including an additional weight value after the specified `Original Weight`.
|
90 |
+
|
91 |
+
For example, if ROUT:X,1,1,1,1,1,1,1,1,1,1,1,X,X,X,X,X and you had a prompt containing \<lora:my_lore:0.5:ROUT:0.7\>. The `X` weights in ROUT would be replaced with `0.7` at runtime.
|
92 |
+
|
93 |
+
> NOTE: If you select an `Original Weight` tag that has a dynamic weight (`X`) and you do not specify a value in the LoRA syntax, it will default to `1`.
|
94 |
+
|
95 |
+
### Save Presets
|
96 |
+
|
97 |
+
The "Save Presets" button saves the text in the current text box. It is better to use a text editor, so use the "Open TextEditor" button to open a text editor, edit the text, and reload it.
|
98 |
+
The text box above the Weights setting is a list of currently available identifiers, useful for copying and pasting into an XY plot. 17 identifiers are required to appear in the list.
|
99 |
+
|
100 |
+
### Fun Usage
|
101 |
+
Used in conjunction with the XY plot, it is possible to examine the impact of each level of the hierarchy.
|
102 |
+
![xy_grid-0017-4285963917](https://user-images.githubusercontent.com/122196982/215341315-493ce5f9-1d6e-4990-a38c-6937e78c6b46.jpg)
|
103 |
+
|
104 |
+
The setting values are as follows.
|
105 |
+
NOT:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
106 |
+
ALL:1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
107 |
+
INS:1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
108 |
+
IND:1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0
|
109 |
+
INALL:1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0
|
110 |
+
MIDD:1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0
|
111 |
+
OUTD:1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0
|
112 |
+
OUTS:1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1
|
113 |
+
OUTALL:1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1
|
114 |
+
|
115 |
+
## XYZ Plotting Function
|
116 |
+
The optimal value can be searched by changing the value of each layer individually.
|
117 |
+
### Usage
|
118 |
+
Check "Active" to activate the function. If Script (such as XYZ plot in Automatic1111) is enabled, it will take precedence.
|
119 |
+
Hires. fix is not supported. batch size is fixed to 1. batch count should be set to 1.
|
120 |
+
Enter XYZ as the identifier of the LoRA that you want to change. It will work even if you do not enter a value corresponding to XYZ in the preset. If a value corresponding to XYZ is entered, that value will be used as the initial value.
|
121 |
+
Inputting ZYX, inverted value will be automatically inputted.
|
122 |
+
This feature enables to match weights of two LoRAs.
|
123 |
+
Inputing XYZ for LoRA1 and ZYX for LoRA2, you get,
|
124 |
+
LoRA1 1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0
|
125 |
+
LoRA2 0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1
|
126 |
+
### Axis type
|
127 |
+
#### values
|
128 |
+
Sets the weight of the hierarchy to be changed. Enter the values separated by commas. 0,0.25,0.5,0.75,1", etc.
|
129 |
+
|
130 |
+
#### Block ID
|
131 |
+
If a block ID is entered, only that block will change to the value specified by value. As with the other types, use commas to separate them. Multiple blocks can be changed at the same time by separating them with a space or hyphen. The initial NOT will invert the change, so NOT IN09-OUT02 will change all blocks except IN09-OUT02.
|
132 |
+
|
133 |
+
#### seed
|
134 |
+
Seed changes, and is intended to be specified on the Z-axis.
|
135 |
+
|
136 |
+
#### Original Weights
|
137 |
+
Specify the initial value to change the weight of each block. If Original Weight is enabled, the value entered for XYZ is ignored.
|
138 |
+
|
139 |
+
### Input example
|
140 |
+
X : value, value : 1,0.25,0.5,0.75,1
|
141 |
+
Y : Block ID, value : BASE,IN01-IN08,IN05-OUT05,OUT03-OUT11,NOT OUT03-OUT11
|
142 |
+
Z : Original Weights, Value : NONE,ALL0.5,ALL
|
143 |
+
|
144 |
+
In this case, an XY plot is created corresponding to the initial values NONE,ALL0.5,ALL.
|
145 |
+
If you select Seed for Z and enter -1,-1,-1, the XY plot will be created 3 times with different seeds.
|
146 |
+
|
147 |
+
### Effective Block Analyzer
|
148 |
+
This function check which layers are working well. The effect of the block is visualized and quantified by setting the intensity of the other bocks to 1, decreasing the intensity of the block you want to examine, and taking the difference.
|
149 |
+
#### Range
|
150 |
+
If you enter 0.5, 1, all initial values are set to 1, and only the target block is calculated as 0.5. Normally, 0.5 will make a difference, but some LoRAs may have difficulty making a difference, in which case, set 0.5 to 0 or a negative value.
|
151 |
+
|
152 |
+
#### settings
|
153 |
+
##### diff color
|
154 |
+
Specify the background color of the diff file.
|
155 |
+
|
156 |
+
##### chnage X-Y
|
157 |
+
Swaps the X and Y axes. By default, Block is assigned to the Y axis.
|
158 |
+
|
159 |
+
##### Threshold
|
160 |
+
Sets the threshold at which a change is recognized when calculating the difference. Basically, the default value is fine, but if you want to detect subtle differences in color, etc., lower the value.
|
161 |
+
|
162 |
+
#### Blocks
|
163 |
+
Enter the blocks to be examined, using the same format as for XYZ plots.
|
164 |
+
|
165 |
+
For more information on block-by-block merging, see
|
166 |
+
|
167 |
+
https://github.com/bbc-mc/sdweb-merge-block-weighted-gui
|
168 |
+
|
169 |
+
# 概要
|
170 |
+
Loraは強力なツールですが、時に扱いが難しく、影響してほしくないところにまで影響がでたりします。このスクリプトではLoraを適用する際、適用度合いをU-Netの階層ごとに設定することができます。これを使用することで求める画像に近づけることができるかもしれません。
|
171 |
+
|
172 |
+
## 使い方
|
173 |
+
scriptフォルダにlora_block_weightを置いてください。 インストール時はWeb-ui.batを再起動をしてください。
|
174 |
+
|
175 |
+
### Active
|
176 |
+
ここにチェックを入れることで動作します。
|
177 |
+
|
178 |
+
### プロンプト
|
179 |
+
プロンプト画面では通常通り使用したいLoraを記入してください。その際、強さの値の次に「:」を入力しウェイトか識別子を入力します。識別子はWeights setting で編集します。
|
180 |
+
```
|
181 |
+
<lora:"lora name":1:0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0>.
|
182 |
+
<lora:"lora name":1:0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>. (a1111-sd-webui-locon, etc.)
|
183 |
+
<lyco:"lora name":1:1:lbw=IN02> (a1111-sd-webui-lycoris)
|
184 |
+
<lyco:"lora name":1:1:lbw=1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0> (a1111-sd-webui-lycoris)
|
185 |
+
```
|
186 |
+
Loraの強さは有効で、階層全体にかかります。大文字と小文字は区別されます。
|
187 |
+
LyCORISに対してLoRAのプリセットも使用できますが、その場合LoRAで使われていない階層のウェイトは1に設定されます。
|
188 |
+
上記の形式になっていない場合プリセットではコメント行として扱われます。
|
189 |
+
a1111-sd-webui-lycoris版のLyCORISを使用する場合構文が異なります。`lbw=IN02`を使って下さい。順番は問いません。その他の書式はlycorisの書式にしたがって下さい。詳しくはLyCORISのドキュメントを参照して下さい。識別子を入力して下さい。a1111-sd-webui-lycoris版は開発途中のためこの構文は変更される可能性があります。
|
190 |
+
|
191 |
+
### Weights setting
|
192 |
+
識別子とウェイトを入力します。
|
193 |
+
フルモデルと異なり、Loraではエンコーダーを含め17のブロックに分かれています。よって、17個の数値を入力してください。
|
194 |
+
BASE,IN,OUTなどはフルモデル相当の階層です。
|
195 |
+
|
196 |
+
|
197 |
+
|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|
|
198 |
+
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
|
199 |
+
|BASE|IN01|IN02|IN04|IN05|IN07|IN08|MID|OUT03|OUT04|OUT05|OUT06|OUT07|OUT08|OUT09|OUT10|OUT11|
|
200 |
+
|
201 |
+
LyCORISなどの場合
|
202 |
+
|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|
|
203 |
+
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
|
204 |
+
|BASE|IN00|IN01|IN02|IN03|IN04|IN05|IN06|IN07|IN08|IN09|IN10|IN11|MID|OUT00|OUT01|OUT02|OUT03|OUT04|OUT05|OUT06|OUT07|OUT08|OUT09|OUT10|OUT11|
|
205 |
+
|
206 |
+
### 特別な値
|
207 |
+
基本的には数値を入れないと正しく動きませんが R および U を入力することでランダムな数値が入力されます。
|
208 |
+
R : 0~1までの小数点3桁の数値
|
209 |
+
U : -1.5~1.5までの小数点3桁の数値
|
210 |
+
|
211 |
+
例えば ROUT:1,1,1,1,1,1,1,1,R,R,R,R,R,R,R,R,R とすると
|
212 |
+
OUT層のみダンダム化されます
|
213 |
+
ランダム化された数値は画像生成時にコマンドプロンプト画面に表示されます
|
214 |
+
|
215 |
+
saveボタンで現在のテキストボックスのテキストを保存できます。テキストエディタを使った方がいいので、open Texteditorボタンでテキストエディタ開き、編集後reloadしてください。
|
216 |
+
Weights settingの上にあるテキストボックスは現在使用できる識別子の一覧です。XYプロットにコピペするのに便利です。17個ないと一覧に表示されません。
|
217 |
+
|
218 |
+
### 楽しい使い方
|
219 |
+
XY plotと併用することで各階層の影響を調べることが可能になります。
|
220 |
+
![xy_grid-0017-4285963917](https://user-images.githubusercontent.com/122196982/215341315-493ce5f9-1d6e-4990-a38c-6937e78c6b46.jpg)
|
221 |
+
|
222 |
+
設定値は以下の通りです。
|
223 |
+
NOT:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
224 |
+
ALL:1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
225 |
+
INS:1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0
|
226 |
+
IND:1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0
|
227 |
+
INALL:1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0
|
228 |
+
MIDD:1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0
|
229 |
+
OUTD:1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0
|
230 |
+
OUTS:1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1
|
231 |
+
OUTALL:1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1
|
232 |
+
|
233 |
+
## XYZ プロット機能
|
234 |
+
各層の値を個別に変化させることで最適値を総当たりに探せます。
|
235 |
+
### 使い方
|
236 |
+
Activeをチェックすることで動作します。 Script(Automatic1111本体のXYZプロットなど)が有効になっている場合そちらが優先されます。noneを選択してください。
|
237 |
+
Hires. fixには対応していません。Batch sizeは1に固定されます。Batch countは1に設定してください。
|
238 |
+
変化させたいLoRAの識別子にXYZと入力します\<lora:"lora名":1:XYZ>。 プリセットにXYZに対応する値を入力していなくても動作します。その場合すべてのウェイトが0の状態からスタートします。XYZに対応する値が入力されている場合はその値が初期値になります。
|
239 |
+
ZYXと入力するとXYZとは反対の値が入力されます。これはふたつのLoRAのウェイトを合わせる際に有効です。
|
240 |
+
例えばLoRA1にXYZ,LoRA2にZYXと入力すると、
|
241 |
+
LoRA1 1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0
|
242 |
+
LoRA2 0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1
|
243 |
+
となります。
|
244 |
+
### 軸タイプ
|
245 |
+
#### values
|
246 |
+
変化させる階層のウェイトを設定します。カンマ区切りで入力してください。「0,0.25,0.5,0.75,1」など。
|
247 |
+
|
248 |
+
#### Block ID
|
249 |
+
ブロックIDを入力すると、そのブロックのみvalueで指定した値に変わります。他のタイプと同様にカンマで区切ります。スペースまたはハイフンで区切ることで複数のブロックを同時に変化させることもできます。最初にNOTをつけることで変化対象が反転します。NOT IN09-OUT02とすると、IN09-OUT02以外が変化します。NOTは最初に入力しないと効果がありません。IN08-M00-OUT03は繋がっています。
|
250 |
+
|
251 |
+
#### Seed
|
252 |
+
シードが変わります。Z軸に指定することを想定��ています。
|
253 |
+
|
254 |
+
#### Original Weights
|
255 |
+
各ブロックのウェイトを変化させる初期値を指定します。プリセットに登録されている識別子を入力してください。Original Weightが有効になっている場合XYZに入力された値は無視されます。
|
256 |
+
|
257 |
+
### 入力例
|
258 |
+
X : value, 値 : 1,0.25,0.5,0.75,1
|
259 |
+
Y : Block ID, 値 : BASE,IN01-IN08,IN05-OUT05,OUT03-OUT11,NOT OUT03-OUT11
|
260 |
+
Z : Original Weights, 値 : NONE,ALL0.5,ALL
|
261 |
+
|
262 |
+
この場合、初期値NONE,ALL0.5,ALLに対応したXY plotが作製されます。
|
263 |
+
ZにSeedを選び、-1,-1,-1を入力すると、異なるseedでXY plotを3回作製します。
|
264 |
+
|
265 |
+
### Effective Block Analyzer
|
266 |
+
どの階層が良く効いているかを判別する機能です。対象の階層以外の強度を1にして、調べたい階層の強度を下げ、差分を取ることで階層の効果を可視化・数値化します。
|
267 |
+
#### Range
|
268 |
+
0.5, 1 と入力した場合、初期値がすべて1になり、対象のブロックのみ0.5として計算が行われます。普通は0.5で差がでますが、LoRAによっては差が出にくい場合があるので、その場合は0.5を0あるいはマイナスの値に設定してください。
|
269 |
+
|
270 |
+
#### 設定
|
271 |
+
##### diff color
|
272 |
+
差分ファイルの背景カラーを指定します。
|
273 |
+
|
274 |
+
##### chnage X-Y
|
275 |
+
X軸とY軸を入れ替えます。デフォルトではY軸にBlockが割り当てられています。
|
276 |
+
|
277 |
+
##### Threshold
|
278 |
+
差分を計算する際の変化したと認識される閾値を設定します。基本的にはデフォルト値で問題ありませんが、微妙な色の違いなどを検出したい場合は値を下げて下さい。
|
279 |
+
|
280 |
+
#### Blocks
|
281 |
+
調べたい階層を入力します。XYZプロットと同じ書式が使用可能です。
|
282 |
+
|
283 |
+
階層別マージについては下記を参照してください
|
284 |
+
|
285 |
+
### elemental
|
286 |
+
詳細は[こちら](https://github.com/hako-mikan/sd-webui-supermerger/blob/main/elemental_ja.md)を参照して下さい。
|
287 |
+
#### 使い方
|
288 |
+
Elementaタブにて階層指定と同じように識別子を設定します。識別子は階層の識別子の後に入力します。
|
289 |
+
\<lora:"lora名":1:IN04:ATTNON>
|
290 |
+
ATTNON:
|
291 |
+
|
292 |
+
書式は
|
293 |
+
識別子:階層指定:要素指定:ウェイト
|
294 |
+
のように指定します。要素は部分一致で判定されます。attn1ならattn1のみ、attnならattn1及びattn2が反応します。階層、要素共に空白で区切ると複数指定できます。
|
295 |
+
print changeをオンにすると反応した要素がコマンドプロンプト上に表示されます。
|
296 |
+
|
297 |
+
ALL0:::0
|
298 |
+
はすべての要素のウェイトをゼロに設定します。
|
299 |
+
IN1:IN00-IN11::1
|
300 |
+
はINのすべての要素を1にします
|
301 |
+
ATTNON::attn:1
|
302 |
+
はすべての階層のattnを1にします。
|
303 |
+
|
304 |
+
#### XYZプロット
|
305 |
+
XYZプロットのelementsの項ではカンマ区切りでXYZプロットが可能になります。
|
306 |
+
その場合は
|
307 |
+
\<lora:"lora名":1:XYZ:XYZ>
|
308 |
+
と指定して下さい。
|
309 |
+
elements
|
310 |
+
の項に
|
311 |
+
IN05-OUT05:attn:0,IN05-OUT05:attn:0.5,IN05-OUT05:attn:1
|
312 |
+
と入力して走らせるとIN05からOUT05までのattnのみを変化させることができます。
|
313 |
+
この際、XYZの値を変更することで初期値を変更できます。デフォルトではelementalのXYZはXYZ:::1となっており、これは全階層、全要素を1にしますが、ここをXYZ:encoder::1とするとテキストエンコーダーのみを有効にした状態で評価ができます。
|
314 |
+
|
315 |
+
https://github.com/bbc-mc/sdweb-merge-block-weighted-gui
|
316 |
+
|
317 |
+
### updates/更新情報
|
318 |
+
2023.02.07 1250(JST)
|
319 |
+
- Changed behavior when XYZ plot Active (Script of the main UI is prioritized).
|
320 |
+
|
321 |
+
2023.02.06 2000(JST)
|
322 |
+
- Feature added: XYZ plotting is added.
|
323 |
+
|
324 |
+
2023.01.31 0200(JST)
|
325 |
+
- Feature added: Random feature is added
|
326 |
+
- Fixed: Weighting now works for negative values.
|
327 |
+
|
328 |
+
2023.02.16 2040(JST)
|
329 |
+
- Original Weight をxやyに設定できない問題を解決しました
|
330 |
+
- Effective Weight Analyzer選択時にXYZのXやYがValuesとBlockIdになっていないとエラーになる問題を解決しました
|
331 |
+
|
332 |
+
2023.02.08 2120(JST)
|
333 |
+
- 階層適応した後通常使用する際、階層適応が残る問題を解決しました
|
334 |
+
- 効果のある階層をワンクリックで判別する機能を追加しました
|
335 |
+
|
336 |
+
2023.02.08 0050(JST)
|
337 |
+
- 一部環境でseedが固定されない問題を解決しました
|
338 |
+
|
339 |
+
2023.02.07 2015(JST)
|
340 |
+
- マイナスのウェイトが正常に働かない問題を修正しました
|
341 |
+
|
342 |
+
2023.02.07 1250(JST)
|
343 |
+
- XYZプロットActive時の動作を変更しました(本体のScriptが優先されるようになります)
|
344 |
+
|
345 |
+
2023.02.06 2000(JST)
|
346 |
+
- 機能追加:XYZプロット機能を追加しました
|
347 |
+
|
348 |
+
2023.01.31 0200(JST)
|
349 |
+
- 機能追加:ランダム機能を追加しました
|
350 |
+
- 機能修正:ウェイトがマイナスにも効くようになりました
|
sd-webui-lora-block-weight/scripts/Roboto-Regular.ttf
ADDED
Binary file (306 kB). View file
|
|
sd-webui-lora-block-weight/scripts/__pycache__/lora_block_weight.cpython-310.pyc
ADDED
Binary file (24.7 kB). View file
|
|
sd-webui-lora-block-weight/scripts/elempresets.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
ATTNDEEPON:IN05-OUT05:attn:1
|
2 |
+
|
3 |
+
ATTNDEEPOFF:IN05-OUT05:attn:0
|
4 |
+
|
5 |
+
PROJDEEPOFF:IN05-OUT05:proj:0
|
6 |
+
|
7 |
+
XYZ:::1
|
sd-webui-lora-block-weight/scripts/lbwpresets.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
NONE:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
2 |
+
ALL:1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
3 |
+
INS:1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0
|
4 |
+
IND:1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0
|
5 |
+
INALL:1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0
|
6 |
+
MIDD:1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0
|
7 |
+
OUTD:1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0
|
8 |
+
OUTS:1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1
|
9 |
+
OUTALL:1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1
|
10 |
+
ALL0.5:0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5
|
sd-webui-lora-block-weight/scripts/lora_block_weight.py
ADDED
@@ -0,0 +1,744 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import os
|
3 |
+
import gc
|
4 |
+
import re
|
5 |
+
import sys
|
6 |
+
import torch
|
7 |
+
import shutil
|
8 |
+
import math
|
9 |
+
import numpy as np
|
10 |
+
import gradio as gr
|
11 |
+
import os.path
|
12 |
+
import random
|
13 |
+
from pprint import pprint
|
14 |
+
import modules.ui
|
15 |
+
import modules.scripts as scripts
|
16 |
+
from PIL import Image, ImageFont, ImageDraw
|
17 |
+
import modules.shared as shared
|
18 |
+
from modules import devices, sd_models, images,extra_networks
|
19 |
+
from modules.shared import opts, state
|
20 |
+
from modules.processing import process_images, Processed
|
21 |
+
|
22 |
+
lxyz = ""
|
23 |
+
lzyx = ""
|
24 |
+
prompts = ""
|
25 |
+
xyelem = ""
|
26 |
+
princ = False
|
27 |
+
|
28 |
+
BLOCKID=["BASE","IN00","IN01","IN02","IN03","IN04","IN05","IN06","IN07","IN08","IN09","IN10","IN11","M00","OUT00","OUT01","OUT02","OUT03","OUT04","OUT05","OUT06","OUT07","OUT08","OUT09","OUT10","OUT11"]
|
29 |
+
|
30 |
+
BLOCKS=["encoder",
|
31 |
+
"diffusion_model_input_blocks_0_",
|
32 |
+
"diffusion_model_input_blocks_1_",
|
33 |
+
"diffusion_model_input_blocks_2_",
|
34 |
+
"diffusion_model_input_blocks_3_",
|
35 |
+
"diffusion_model_input_blocks_4_",
|
36 |
+
"diffusion_model_input_blocks_5_",
|
37 |
+
"diffusion_model_input_blocks_6_",
|
38 |
+
"diffusion_model_input_blocks_7_",
|
39 |
+
"diffusion_model_input_blocks_8_",
|
40 |
+
"diffusion_model_input_blocks_9_",
|
41 |
+
"diffusion_model_input_blocks_10_",
|
42 |
+
"diffusion_model_input_blocks_11_",
|
43 |
+
"diffusion_model_middle_block_",
|
44 |
+
"diffusion_model_output_blocks_0_",
|
45 |
+
"diffusion_model_output_blocks_1_",
|
46 |
+
"diffusion_model_output_blocks_2_",
|
47 |
+
"diffusion_model_output_blocks_3_",
|
48 |
+
"diffusion_model_output_blocks_4_",
|
49 |
+
"diffusion_model_output_blocks_5_",
|
50 |
+
"diffusion_model_output_blocks_6_",
|
51 |
+
"diffusion_model_output_blocks_7_",
|
52 |
+
"diffusion_model_output_blocks_8_",
|
53 |
+
"diffusion_model_output_blocks_9_",
|
54 |
+
"diffusion_model_output_blocks_10_",
|
55 |
+
"diffusion_model_output_blocks_11_"]
|
56 |
+
|
57 |
+
loopstopper = True
|
58 |
+
|
59 |
+
ATYPES =["none","Block ID","values","seed","Original Weights","elements"]
|
60 |
+
|
61 |
+
DEF_WEIGHT_PRESET = "\
|
62 |
+
NONE:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n\
|
63 |
+
ALL:1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1\n\
|
64 |
+
INS:1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0\n\
|
65 |
+
IND:1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0\n\
|
66 |
+
INALL:1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0\n\
|
67 |
+
MIDD:1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0\n\
|
68 |
+
OUTD:1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0\n\
|
69 |
+
OUTS:1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1\n\
|
70 |
+
OUTALL:1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1\n\
|
71 |
+
ALL0.5:0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5"
|
72 |
+
|
73 |
+
class Script(modules.scripts.Script):
|
74 |
+
def title(self):
|
75 |
+
return "LoRA Block Weight"
|
76 |
+
|
77 |
+
def show(self, is_img2img):
|
78 |
+
return modules.scripts.AlwaysVisible
|
79 |
+
|
80 |
+
def ui(self, is_img2img):
|
81 |
+
import lora
|
82 |
+
LWEIGHTSPRESETS = DEF_WEIGHT_PRESET
|
83 |
+
|
84 |
+
runorigin = scripts.scripts_txt2img.run
|
85 |
+
runorigini = scripts.scripts_img2img.run
|
86 |
+
|
87 |
+
path_root = scripts.basedir()
|
88 |
+
extpath = os.path.join(path_root,"extensions","sd-webui-lora-block-weight","scripts", "lbwpresets.txt")
|
89 |
+
extpathe = os.path.join(path_root,"extensions","sd-webui-lora-block-weight","scripts", "elempresets.txt")
|
90 |
+
filepath = os.path.join(path_root,"scripts", "lbwpresets.txt")
|
91 |
+
filepathe = os.path.join(path_root,"scripts", "elempresets.txt")
|
92 |
+
|
93 |
+
if os.path.isfile(filepath) and not os.path.isfile(extpath):
|
94 |
+
shutil.move(filepath,extpath)
|
95 |
+
|
96 |
+
if os.path.isfile(filepathe) and not os.path.isfile(extpathe):
|
97 |
+
shutil.move(filepathe,extpathe)
|
98 |
+
|
99 |
+
lbwpresets=""
|
100 |
+
|
101 |
+
try:
|
102 |
+
with open(extpath,encoding="utf-8") as f:
|
103 |
+
lbwpresets = f.read()
|
104 |
+
except OSError as e:
|
105 |
+
lbwpresets=LWEIGHTSPRESETS
|
106 |
+
if not os.path.isfile(extpath):
|
107 |
+
try:
|
108 |
+
with open(extpath,mode = 'w',encoding="utf-8") as f:
|
109 |
+
f.write(lbwpresets)
|
110 |
+
except:
|
111 |
+
pass
|
112 |
+
|
113 |
+
try:
|
114 |
+
with open(extpathe,encoding="utf-8") as f:
|
115 |
+
elempresets = f.read()
|
116 |
+
except OSError as e:
|
117 |
+
elempresets=ELEMPRESETS
|
118 |
+
if not os.path.isfile(extpathe):
|
119 |
+
try:
|
120 |
+
with open(extpathe,mode = 'w',encoding="utf-8") as f:
|
121 |
+
f.write(elempresets)
|
122 |
+
except:
|
123 |
+
pass
|
124 |
+
|
125 |
+
loraratios=lbwpresets.splitlines()
|
126 |
+
lratios={}
|
127 |
+
for i,l in enumerate(loraratios):
|
128 |
+
if ":" not in l or not (l.count(",") == 16 or l.count(",") == 25) : continue
|
129 |
+
lratios[l.split(":")[0]]=l.split(":")[1]
|
130 |
+
ratiostags = [k for k in lratios.keys()]
|
131 |
+
ratiostags = ",".join(ratiostags)
|
132 |
+
|
133 |
+
with gr.Accordion("LoRA Block Weight",open = False):
|
134 |
+
with gr.Row():
|
135 |
+
with gr.Column(min_width = 50, scale=1):
|
136 |
+
lbw_useblocks = gr.Checkbox(value = True,label="Active",interactive =True,elem_id="lbw_active")
|
137 |
+
with gr.Column(scale=5):
|
138 |
+
bw_ratiotags= gr.TextArea(label="",value=ratiostags,visible =True,interactive =True,elem_id="lbw_ratios")
|
139 |
+
with gr.Accordion("XYZ plot",open = False):
|
140 |
+
gr.HTML(value='<p style= "word-wrap:break-word;">changeable blocks : BASE,IN00,IN01,IN02,IN03,IN04,IN05,IN06,IN07,IN08,IN09,IN10,IN11,M00,OUT00,OUT01,OUT02,OUT03,OUT04,OUT05,OUT06,OUT07,OUT08,OUT09,OUT10,OUT11</p>')
|
141 |
+
xyzsetting = gr.Radio(label = "Active",choices = ["Disable","XYZ plot","Effective Block Analyzer"], value ="Disable",type = "index")
|
142 |
+
with gr.Row(visible = False) as esets:
|
143 |
+
diffcol = gr.Radio(label = "diff image color",choices = ["black","white"], value ="black",type = "value",interactive =True)
|
144 |
+
revxy = gr.Checkbox(value = False,label="change X-Y",interactive =True,elem_id="lbw_changexy")
|
145 |
+
thresh = gr.Textbox(label="difference threshold",lines=1,value="20",interactive =True,elem_id="diff_thr")
|
146 |
+
xtype = gr.Dropdown(label="X Types ", choices=[x for x in ATYPES], value=ATYPES [2],interactive =True,elem_id="lbw_xtype")
|
147 |
+
xmen = gr.Textbox(label="X Values ",lines=1,value="0,0.25,0.5,0.75,1",interactive =True,elem_id="lbw_xmen")
|
148 |
+
ytype = gr.Dropdown(label="Y Types ", choices=[y for y in ATYPES], value=ATYPES [1],interactive =True,elem_id="lbw_ytype")
|
149 |
+
ymen = gr.Textbox(label="Y Values " ,lines=1,value="IN05-OUT05",interactive =True,elem_id="lbw_ymen")
|
150 |
+
ztype = gr.Dropdown(label="Z type ", choices=[z for z in ATYPES], value=ATYPES[0],interactive =True,elem_id="lbw_ztype")
|
151 |
+
zmen = gr.Textbox(label="Z values ",lines=1,value="",interactive =True,elem_id="lbw_zmen")
|
152 |
+
|
153 |
+
exmen = gr.Textbox(label="Range",lines=1,value="0.5,1",interactive =True,elem_id="lbw_exmen",visible = False)
|
154 |
+
eymen = gr.Textbox(label="Blocks" ,lines=1,value="BASE,IN00,IN01,IN02,IN03,IN04,IN05,IN06,IN07,IN08,IN09,IN10,IN11,M00,OUT00,OUT01,OUT02,OUT03,OUT04,OUT05,OUT06,OUT07,OUT08,OUT09,OUT10,OUT11",interactive =True,elem_id="lbw_eymen",visible = False)
|
155 |
+
ecount = gr.Number(value=1, label="number of seed", interactive=True, visible = True)
|
156 |
+
|
157 |
+
with gr.Accordion("Weights setting",open = True):
|
158 |
+
with gr.Row():
|
159 |
+
reloadtext = gr.Button(value="Reload Presets",variant='primary',elem_id="lbw_reload")
|
160 |
+
reloadtags = gr.Button(value="Reload Tags",variant='primary',elem_id="lbw_reload")
|
161 |
+
savetext = gr.Button(value="Save Presets",variant='primary',elem_id="lbw_savetext")
|
162 |
+
openeditor = gr.Button(value="Open TextEditor",variant='primary',elem_id="lbw_openeditor")
|
163 |
+
lbw_loraratios = gr.TextArea(label="",value=lbwpresets,visible =True,interactive = True,elem_id="lbw_ratiospreset")
|
164 |
+
|
165 |
+
with gr.Accordion("Elemental",open = False):
|
166 |
+
with gr.Row():
|
167 |
+
e_reloadtext = gr.Button(value="Reload Presets",variant='primary',elem_id="lbw_reload")
|
168 |
+
e_savetext = gr.Button(value="Save Presets",variant='primary',elem_id="lbw_savetext")
|
169 |
+
e_openeditor = gr.Button(value="Open TextEditor",variant='primary',elem_id="lbw_openeditor")
|
170 |
+
elemsets = gr.Checkbox(value = False,label="print change",interactive =True,elem_id="lbw_print_change")
|
171 |
+
elemental = gr.TextArea(label="Identifer:BlockID:Elements:Ratio,...,separated by empty line ",value = elempresets,interactive =True,elem_id="element")
|
172 |
+
|
173 |
+
d_true = gr.Checkbox(value = True,visible = False)
|
174 |
+
d_false = gr.Checkbox(value = False,visible = False)
|
175 |
+
|
176 |
+
import subprocess
|
177 |
+
def openeditors(b):
|
178 |
+
path = extpath if b else extpathe
|
179 |
+
subprocess.Popen(['start', path], shell=True)
|
180 |
+
|
181 |
+
def reloadpresets(isweight):
|
182 |
+
if isweight:
|
183 |
+
try:
|
184 |
+
with open(extpath,encoding="utf-8") as f:
|
185 |
+
return f.read()
|
186 |
+
except OSError as e:
|
187 |
+
pass
|
188 |
+
else:
|
189 |
+
try:
|
190 |
+
with open(extpath,encoding="utf-8") as f:
|
191 |
+
return f.read()
|
192 |
+
except OSError as e:
|
193 |
+
pass
|
194 |
+
|
195 |
+
def tagdicter(presets):
|
196 |
+
presets=presets.splitlines()
|
197 |
+
wdict={}
|
198 |
+
for l in presets:
|
199 |
+
if ":" not in l or not (l.count(",") == 16 or l.count(",") == 25) : continue
|
200 |
+
w=[]
|
201 |
+
if ":" in l :
|
202 |
+
key = l.split(":",1)[0]
|
203 |
+
w = l.split(":",1)[1]
|
204 |
+
if len([w for w in w.split(",")]) == 17 or len([w for w in w.split(",")]) ==26:
|
205 |
+
wdict[key.strip()]=w
|
206 |
+
return ",".join(list(wdict.keys()))
|
207 |
+
|
208 |
+
def savepresets(text,isweight):
|
209 |
+
if isweight:
|
210 |
+
with open(extpath,mode = 'w',encoding="utf-8") as f:
|
211 |
+
f.write(text)
|
212 |
+
else:
|
213 |
+
with open(extpathe,mode = 'w',encoding="utf-8") as f:
|
214 |
+
f.write(text)
|
215 |
+
|
216 |
+
reloadtext.click(fn=reloadpresets,inputs=[d_true],outputs=[lbw_loraratios])
|
217 |
+
reloadtags.click(fn=tagdicter,inputs=[lbw_loraratios],outputs=[bw_ratiotags])
|
218 |
+
savetext.click(fn=savepresets,inputs=[lbw_loraratios,d_true],outputs=[])
|
219 |
+
openeditor.click(fn=openeditors,inputs=[d_true],outputs=[])
|
220 |
+
|
221 |
+
e_reloadtext.click(fn=reloadpresets,inputs=[d_false],outputs=[elemental])
|
222 |
+
e_savetext.click(fn=savepresets,inputs=[elemental,d_false],outputs=[])
|
223 |
+
e_openeditor.click(fn=openeditors,inputs=[d_false],outputs=[])
|
224 |
+
|
225 |
+
def urawaza(active):
|
226 |
+
if active > 0:
|
227 |
+
for obj in scripts.scripts_txt2img.alwayson_scripts:
|
228 |
+
if "lora_block_weight" in obj.filename:
|
229 |
+
scripts.scripts_txt2img.selectable_scripts.append(obj)
|
230 |
+
scripts.scripts_txt2img.titles.append("LoRA Block Weight")
|
231 |
+
for obj in scripts.scripts_img2img.alwayson_scripts:
|
232 |
+
if "lora_block_weight" in obj.filename:
|
233 |
+
scripts.scripts_img2img.selectable_scripts.append(obj)
|
234 |
+
scripts.scripts_img2img.titles.append("LoRA Block Weight")
|
235 |
+
scripts.scripts_txt2img.run = newrun
|
236 |
+
scripts.scripts_img2img.run = newrun
|
237 |
+
if active == 1:return [*[gr.update(visible = True) for x in range(6)],*[gr.update(visible = False) for x in range(4)]]
|
238 |
+
else:return [*[gr.update(visible = False) for x in range(6)],*[gr.update(visible = True) for x in range(4)]]
|
239 |
+
else:
|
240 |
+
scripts.scripts_txt2img.run = runorigin
|
241 |
+
scripts.scripts_img2img.run = runorigini
|
242 |
+
return [*[gr.update(visible = True) for x in range(6)],*[gr.update(visible = False) for x in range(4)]]
|
243 |
+
|
244 |
+
xyzsetting.change(fn=urawaza,inputs=[xyzsetting],outputs =[xtype,xmen,ytype,ymen,ztype,zmen,exmen,eymen,ecount,esets])
|
245 |
+
|
246 |
+
return lbw_loraratios,lbw_useblocks,xyzsetting,xtype,xmen,ytype,ymen,ztype,zmen,exmen,eymen,ecount,diffcol,thresh,revxy,elemental,elemsets
|
247 |
+
|
248 |
+
def process(self, p, loraratios,useblocks,xyzsetting,xtype,xmen,ytype,ymen,ztype,zmen,exmen,eymen,ecount,diffcol,thresh,revxy,elemental,elemsets):
|
249 |
+
#print("self =",self,"p =",p,"presets =",loraratios,"useblocks =",useblocks,"xyzsettings =",xyzsetting,"xtype =",xtype,"xmen =",xmen,"ytype =",ytype,"ymen =",ymen,"ztype =",ztype,"zmen =",zmen)
|
250 |
+
#Note that this does not use the default arg syntax because the default args are supposed to be at the end of the function
|
251 |
+
if(loraratios == None):
|
252 |
+
loraratios = DEF_WEIGHT_PRESET
|
253 |
+
if(useblocks == None):
|
254 |
+
useblocks = True
|
255 |
+
|
256 |
+
if useblocks:
|
257 |
+
loraratios=loraratios.splitlines()
|
258 |
+
elemental = elemental.split("\n\n") if elemental is not None else []
|
259 |
+
lratios={}
|
260 |
+
elementals={}
|
261 |
+
for l in loraratios:
|
262 |
+
if ":" not in l or not (l.count(",") == 16 or l.count(",") == 25) : continue
|
263 |
+
l0=l.split(":",1)[0]
|
264 |
+
lratios[l0.strip()]=l.split(":",1)[1]
|
265 |
+
for e in elemental:
|
266 |
+
e0=e.split(":",1)[0]
|
267 |
+
elementals[e0.strip()]=e.split(":",1)[1]
|
268 |
+
if elemsets : print(xyelem)
|
269 |
+
if xyzsetting and "XYZ" in p.prompt:
|
270 |
+
lratios["XYZ"] = lxyz
|
271 |
+
lratios["ZYX"] = lzyx
|
272 |
+
if xyelem != "":
|
273 |
+
if "XYZ" in elementals.keys():
|
274 |
+
elementals["XYZ"] = elementals["XYZ"] + ","+ xyelem
|
275 |
+
else:
|
276 |
+
elementals["XYZ"] = xyelem
|
277 |
+
self.lratios = lratios
|
278 |
+
self.elementals = elementals
|
279 |
+
global princ
|
280 |
+
princ = elemsets
|
281 |
+
return
|
282 |
+
|
283 |
+
def before_process_batch(self, p, loraratios,useblocks,xyzsetting,xtype,xmen,ytype,ymen,ztype,zmen,exmen,eymen,ecount,diffcol,thresh,revxy,elemental,elemsets,**kwargs):
|
284 |
+
if useblocks:
|
285 |
+
global prompts
|
286 |
+
prompts = kwargs["prompts"].copy()
|
287 |
+
|
288 |
+
def process_batch(self, p, loraratios,useblocks,xyzsetting,xtype,xmen,ytype,ymen,ztype,zmen,exmen,eymen,ecount,diffcol,thresh,revxy,elemental,elemsets,**kwargs):
|
289 |
+
if useblocks:
|
290 |
+
o_prompts = [p.prompt]
|
291 |
+
for prompt in prompts:
|
292 |
+
if "<lora" in prompt or "<lyco" in prompt:
|
293 |
+
o_prompts = prompts.copy()
|
294 |
+
loradealer(o_prompts ,self.lratios,self.elementals)
|
295 |
+
|
296 |
+
def postprocess(self, p, processed, *args):
|
297 |
+
import lora
|
298 |
+
lora.loaded_loras.clear()
|
299 |
+
global lxyz,lzyx,xyelem
|
300 |
+
lxyz = lzyx = xyelem = ""
|
301 |
+
gc.collect()
|
302 |
+
|
303 |
+
def run(self,p,presets,useblocks,xyzsetting,xtype,xmen,ytype,ymen,ztype,zmen,exmen,eymen,ecount,diffcol,thresh,revxy,elemental,elemsets):
|
304 |
+
if xyzsetting >0:
|
305 |
+
import lora
|
306 |
+
loraratios=presets.splitlines()
|
307 |
+
lratios={}
|
308 |
+
for l in loraratios:
|
309 |
+
if ":" not in l or not (l.count(",") == 16 or l.count(",") == 25) : continue
|
310 |
+
l0=l.split(":",1)[0]
|
311 |
+
lratios[l0.strip()]=l.split(":",1)[1]
|
312 |
+
|
313 |
+
if "XYZ" in p.prompt:
|
314 |
+
base = lratios["XYZ"] if "XYZ" in lratios.keys() else "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"
|
315 |
+
else: return
|
316 |
+
|
317 |
+
if xyzsetting > 1:
|
318 |
+
xmen,ymen = exmen,eymen
|
319 |
+
xtype,ytype = "values","ID"
|
320 |
+
ebase = xmen.split(",")[1]
|
321 |
+
ebase = [ebase.strip()]*26
|
322 |
+
base = ",".join(ebase)
|
323 |
+
ztype = ""
|
324 |
+
if ecount > 1:
|
325 |
+
ztype = "seed"
|
326 |
+
zmen = ",".join([str(random.randrange(4294967294)) for x in range(int(ecount))])
|
327 |
+
|
328 |
+
#ATYPES =["none","Block ID","values","seed","Base Weights"]
|
329 |
+
|
330 |
+
def dicedealer(am):
|
331 |
+
for i,a in enumerate(am):
|
332 |
+
if a =="-1": am[i] = str(random.randrange(4294967294))
|
333 |
+
print(f"the die was thrown : {am}")
|
334 |
+
|
335 |
+
if p.seed == -1: p.seed = str(random.randrange(4294967294))
|
336 |
+
|
337 |
+
#print(f"xs:{xmen},ys:{ymen},zs:{zmen}")
|
338 |
+
|
339 |
+
def adjuster(a,at):
|
340 |
+
if "none" in at:a = ""
|
341 |
+
a = [a.strip() for a in a.split(',')]
|
342 |
+
if "seed" in at:dicedealer(a)
|
343 |
+
return a
|
344 |
+
|
345 |
+
xs = adjuster(xmen,xtype)
|
346 |
+
ys = adjuster(ymen,ytype)
|
347 |
+
zs = adjuster(zmen,ztype)
|
348 |
+
|
349 |
+
ids = alpha =seed = ""
|
350 |
+
p.batch_size = 1
|
351 |
+
|
352 |
+
print(f"xs:{xs},ys:{ys},zs:{zs}")
|
353 |
+
|
354 |
+
images = []
|
355 |
+
|
356 |
+
def weightsdealer(alpha,ids,base):
|
357 |
+
blockid17=["BASE","IN01","IN02","IN04","IN05","IN07","IN08","M00","OUT03","OUT04","OUT05","OUT06","OUT07","OUT08","OUT09","OUT10","OUT11"]
|
358 |
+
blockid26=["BASE","IN00","IN01","IN02","IN03","IN04","IN05","IN06","IN07","IN08","IN09","IN10","IN11","M00","OUT00","OUT01","OUT02","OUT03","OUT04","OUT05","OUT06","OUT07","OUT08","OUT09","OUT10","OUT11"]
|
359 |
+
#print(f"weights from : {base}")
|
360 |
+
ids = [z.strip() for z in ids.split(' ')]
|
361 |
+
weights_t = [w.strip() for w in base.split(',')]
|
362 |
+
blockid = blockid17 if len(weights_t) ==17 else blockid26
|
363 |
+
if ids[0]!="NOT":
|
364 |
+
flagger=[False]*len(weights_t)
|
365 |
+
changer = True
|
366 |
+
else:
|
367 |
+
flagger=[True]*len(weights_t)
|
368 |
+
changer = False
|
369 |
+
for id in ids:
|
370 |
+
if id =="NOT":continue
|
371 |
+
if "-" in id:
|
372 |
+
it = [it.strip() for it in id.split('-')]
|
373 |
+
if blockid.index(it[1]) > blockid.index(it[0]):
|
374 |
+
flagger[blockid.index(it[0]):blockid.index(it[1])+1] = [changer]*(blockid.index(it[1])-blockid.index(it[0])+1)
|
375 |
+
else:
|
376 |
+
flagger[blockid.index(it[1]):blockid.index(it[0])+1] = [changer]*(blockid.index(it[0])-blockid.index(it[1])+1)
|
377 |
+
else:
|
378 |
+
flagger[blockid.index(id)] =changer
|
379 |
+
for i,f in enumerate(flagger):
|
380 |
+
if f:weights_t[i]=alpha
|
381 |
+
outext = ",".join(weights_t)
|
382 |
+
#print(f"weights changed: {outext}")
|
383 |
+
return outext
|
384 |
+
|
385 |
+
def xyzdealer(a,at):
|
386 |
+
nonlocal ids,alpha,p,base,c_base
|
387 |
+
if "ID" in at:return
|
388 |
+
if "values" in at:alpha = a
|
389 |
+
if "seed" in at:
|
390 |
+
p.seed = int(a)
|
391 |
+
if "Weights" in at:base =c_base = lratios[a]
|
392 |
+
if "elements" in at:
|
393 |
+
global xyelem
|
394 |
+
xyelem = a
|
395 |
+
|
396 |
+
grids = []
|
397 |
+
images =[]
|
398 |
+
|
399 |
+
totalcount = len(xs)*len(ys)*len(zs) if xyzsetting < 2 else len(xs)*len(ys)*len(zs) //2 +1
|
400 |
+
shared.total_tqdm.updateTotal(totalcount)
|
401 |
+
xc = yc =zc = 0
|
402 |
+
state.job_count = totalcount
|
403 |
+
totalcount = len(xs)*len(ys)*len(zs)
|
404 |
+
c_base = base
|
405 |
+
|
406 |
+
for z in zs:
|
407 |
+
images = []
|
408 |
+
yc = 0
|
409 |
+
xyzdealer(z,ztype)
|
410 |
+
for y in ys:
|
411 |
+
xc = 0
|
412 |
+
xyzdealer(y,ytype)
|
413 |
+
for x in xs:
|
414 |
+
xyzdealer(x,xtype)
|
415 |
+
if "ID" in xtype:
|
416 |
+
if "values" in ytype:c_base = weightsdealer(y,x,base)
|
417 |
+
if "values" in ztype:c_base = weightsdealer(z,x,base)
|
418 |
+
if "ID" in ytype:
|
419 |
+
if "values" in xtype:c_base = weightsdealer(x,y,base)
|
420 |
+
if "values" in ztype:c_base = weightsdealer(z,y,base)
|
421 |
+
if "ID" in ztype:
|
422 |
+
if "values" in xtype:c_base = weightsdealer(x,z,base)
|
423 |
+
if "values" in ytype:c_base = weightsdealer(y,z,base)
|
424 |
+
|
425 |
+
print(f"X:{xtype}, {x},Y: {ytype},{y}, Z:{ztype},{z}, base:{c_base} ({len(xs)*len(ys)*zc + yc*len(xs) +xc +1}/{totalcount})")
|
426 |
+
|
427 |
+
global lxyz,lzyx
|
428 |
+
lxyz = c_base
|
429 |
+
|
430 |
+
cr_base = c_base.split(",")
|
431 |
+
cr_base_t=[]
|
432 |
+
for x in cr_base:
|
433 |
+
if not identifier(x):
|
434 |
+
cr_base_t.append(str(1-float(x)))
|
435 |
+
else:
|
436 |
+
cr_base_t.append(x)
|
437 |
+
lzyx = ",".join(cr_base_t)
|
438 |
+
|
439 |
+
if not(xc == 1 and not (yc ==0 ) and xyzsetting >1):
|
440 |
+
lora.loaded_loras.clear()
|
441 |
+
processed:Processed = process_images(p)
|
442 |
+
images.append(processed.images[0])
|
443 |
+
xc += 1
|
444 |
+
yc += 1
|
445 |
+
zc += 1
|
446 |
+
origin = loranames(processed.all_prompts) + ", "+ znamer(ztype,z,base)
|
447 |
+
images,xst,yst = effectivechecker(images,xs.copy(),ys.copy(),diffcol,thresh,revxy) if xyzsetting >1 else (images,xs.copy(),ys.copy())
|
448 |
+
grids.append(smakegrid(images,xst,yst,origin,p))
|
449 |
+
processed.images= grids
|
450 |
+
lora.loaded_loras.clear()
|
451 |
+
return processed
|
452 |
+
|
453 |
+
def identifier(char):
|
454 |
+
return char[0] in ["R", "U", "X"]
|
455 |
+
|
456 |
+
def znamer(at,a,base):
|
457 |
+
if "ID" in at:return f"Block : {a}"
|
458 |
+
if "values" in at:return f"value : {a}"
|
459 |
+
if "seed" in at:return f"seed : {a}"
|
460 |
+
if "Weights" in at:return f"original weights :\n {base}"
|
461 |
+
else: return ""
|
462 |
+
|
463 |
+
def loranames(all_prompts):
|
464 |
+
_, extra_network_data = extra_networks.parse_prompts(all_prompts[0:1])
|
465 |
+
calledloras = extra_network_data["lora"] if "lyco" not in extra_network_data.keys() else extra_network_data["lyco"]
|
466 |
+
names = ""
|
467 |
+
for called in calledloras:
|
468 |
+
if len(called.items) <3:continue
|
469 |
+
names += called.items[0]
|
470 |
+
return names
|
471 |
+
|
472 |
+
def lycodealer(called):
|
473 |
+
for item in called.items[1:]:
|
474 |
+
if "lbw" in item:
|
475 |
+
called.items[2] = item.split("=")[1]
|
476 |
+
return called
|
477 |
+
|
478 |
+
def loradealer(prompts,lratios,elementals):
|
479 |
+
_, extra_network_data = extra_networks.parse_prompts(prompts)
|
480 |
+
moduletypes = extra_network_data.keys()
|
481 |
+
|
482 |
+
for ltype in moduletypes:
|
483 |
+
lorans = []
|
484 |
+
lorars = []
|
485 |
+
multipliers = []
|
486 |
+
elements = []
|
487 |
+
if not (ltype == "lora" or ltype == "lyco") : continue
|
488 |
+
for called in extra_network_data[ltype]:
|
489 |
+
if ltype == "lyco":
|
490 |
+
called = lycodealer(called)
|
491 |
+
multiple = float(called.items[1])
|
492 |
+
multipliers.append(multiple)
|
493 |
+
if len(called.items) <3:
|
494 |
+
continue
|
495 |
+
lorans.append(called.items[0])
|
496 |
+
if called.items[2] in lratios or called.items[2].count(",") ==16 or called.items[2].count(",") ==25:
|
497 |
+
wei = lratios[called.items[2]] if called.items[2] in lratios else called.items[2]
|
498 |
+
ratios = [w.strip() for w in wei.split(",")]
|
499 |
+
for i,r in enumerate(ratios):
|
500 |
+
if r =="R":
|
501 |
+
ratios[i] = round(random.random(),3)
|
502 |
+
elif r == "U":
|
503 |
+
ratios[i] = round(random.uniform(-0.5,1.5),3)
|
504 |
+
elif r[0] == "X":
|
505 |
+
base = called.items[3] if len(called.items) >= 4 else 1
|
506 |
+
ratios[i] = getinheritedweight(base, r)
|
507 |
+
else:
|
508 |
+
ratios[i] = float(r)
|
509 |
+
print(f"LoRA Block weight ({ltype}): {called.items[0]}: {multiple} x {[x for x in ratios]}")
|
510 |
+
if len(ratios)==17:
|
511 |
+
ratios = [ratios[0]] + [1] + ratios[1:3]+ [1] + ratios[3:5]+[1] + ratios[5:7]+[1,1,1] + [ratios[7]] + [1,1,1] + ratios[8:]
|
512 |
+
lorars.append(ratios)
|
513 |
+
if len(called.items) > 3:
|
514 |
+
if called.items[3] in elementals:
|
515 |
+
elements.append(elementals[called.items[3]])
|
516 |
+
else:
|
517 |
+
elements.append(called.items[3])
|
518 |
+
else:
|
519 |
+
elements.append("")
|
520 |
+
if len(lorars) > 0: load_loras_blocks(lorans,lorars,multipliers,elements,ltype)
|
521 |
+
|
522 |
+
def isfloat(t):
|
523 |
+
try:
|
524 |
+
float(t)
|
525 |
+
return True
|
526 |
+
except:
|
527 |
+
return False
|
528 |
+
|
529 |
+
re_inherited_weight = re.compile(r"X([+-])?([\d.]+)?")
|
530 |
+
|
531 |
+
def getinheritedweight(weight, offset):
|
532 |
+
match = re_inherited_weight.search(offset)
|
533 |
+
if match.group(1) == "+":
|
534 |
+
return float(weight) + float(match.group(2))
|
535 |
+
elif match.group(1) == "-":
|
536 |
+
return float(weight) - float(match.group(2))
|
537 |
+
else:
|
538 |
+
return float(weight)
|
539 |
+
|
540 |
+
def load_loras_blocks(names, lwei,multipliers,elements = [],ltype = "lora"):
|
541 |
+
if "lora" == ltype:
|
542 |
+
print(names,lwei,elements)
|
543 |
+
import lora
|
544 |
+
for l, loaded in enumerate(lora.loaded_loras):
|
545 |
+
for n, name in enumerate(names):
|
546 |
+
if name == loaded.name:
|
547 |
+
lbw(lora.loaded_loras[l],lwei[n],elements[n])
|
548 |
+
lora.loaded_loras[l].name = lora.loaded_loras[l].name +"added_by_lora_block_weight"+ str(random.random())
|
549 |
+
|
550 |
+
elif "lyco" == ltype:
|
551 |
+
import lycoris as lycomo
|
552 |
+
for l, loaded in enumerate(lycomo.loaded_lycos):
|
553 |
+
for n, name in enumerate(names):
|
554 |
+
if name == loaded.name:
|
555 |
+
lbw(lycomo.loaded_lycos[l],lwei[n],elements[n])
|
556 |
+
lycomo.loaded_lycos[l].name = lycomo.loaded_lycos[l].name +"added_by_lora_block_weight"+ str(random.random())
|
557 |
+
|
558 |
+
def smakegrid(imgs,xs,ys,currentmodel,p):
|
559 |
+
ver_texts = [[images.GridAnnotation(y)] for y in ys]
|
560 |
+
hor_texts = [[images.GridAnnotation(x)] for x in xs]
|
561 |
+
|
562 |
+
w, h = imgs[0].size
|
563 |
+
grid = Image.new('RGB', size=(len(xs) * w, len(ys) * h), color='black')
|
564 |
+
|
565 |
+
for i, img in enumerate(imgs):
|
566 |
+
grid.paste(img, box=(i % len(xs) * w, i // len(xs) * h))
|
567 |
+
|
568 |
+
grid = images.draw_grid_annotations(grid,w, h, hor_texts, ver_texts)
|
569 |
+
grid = draw_origin(grid, currentmodel,w*len(xs),h*len(ys),w)
|
570 |
+
if opts.grid_save:
|
571 |
+
images.save_image(grid, opts.outdir_txt2img_grids, "xy_grid", extension=opts.grid_format, prompt=p.prompt, seed=p.seed, grid=True, p=p)
|
572 |
+
|
573 |
+
return grid
|
574 |
+
|
575 |
+
def get_font(fontsize):
|
576 |
+
path_root = scripts.basedir()
|
577 |
+
fontpath = os.path.join(path_root,"extensions","sd-webui-lora-block-weight","scripts", "Roboto-Regular.ttf")
|
578 |
+
try:
|
579 |
+
return ImageFont.truetype(opts.font or fontpath, fontsize)
|
580 |
+
except Exception:
|
581 |
+
return ImageFont.truetype(fontpath, fontsize)
|
582 |
+
|
583 |
+
def draw_origin(grid, text,width,height,width_one):
|
584 |
+
grid_d= Image.new("RGB", (grid.width,grid.height), "white")
|
585 |
+
grid_d.paste(grid,(0,0))
|
586 |
+
|
587 |
+
d= ImageDraw.Draw(grid_d)
|
588 |
+
color_active = (0, 0, 0)
|
589 |
+
fontsize = (width+height)//25
|
590 |
+
fnt = get_font(fontsize)
|
591 |
+
|
592 |
+
if grid.width != width_one:
|
593 |
+
while d.multiline_textsize(text, font=fnt)[0] > width_one*0.75 and fontsize > 0:
|
594 |
+
fontsize -=1
|
595 |
+
fnt = get_font(fontsize)
|
596 |
+
d.multiline_text((0,0), text, font=fnt, fill=color_active,align="center")
|
597 |
+
return grid_d
|
598 |
+
|
599 |
+
def newrun(p, *args):
|
600 |
+
script_index = args[0]
|
601 |
+
|
602 |
+
if args[0] ==0:
|
603 |
+
script = None
|
604 |
+
for obj in scripts.scripts_txt2img.alwayson_scripts:
|
605 |
+
if "lora_block_weight" in obj.filename:
|
606 |
+
script = obj
|
607 |
+
script_args = args[script.args_from:script.args_to]
|
608 |
+
else:
|
609 |
+
script = scripts.scripts_txt2img.selectable_scripts[script_index-1]
|
610 |
+
|
611 |
+
if script is None:
|
612 |
+
return None
|
613 |
+
|
614 |
+
script_args = args[script.args_from:script.args_to]
|
615 |
+
|
616 |
+
processed = script.run(p, *script_args)
|
617 |
+
|
618 |
+
shared.total_tqdm.clear()
|
619 |
+
|
620 |
+
return processed
|
621 |
+
|
622 |
+
def effectivechecker(imgs,ss,ls,diffcol,thresh,revxy):
|
623 |
+
diffs = []
|
624 |
+
outnum =[]
|
625 |
+
imgs[0],imgs[1] = imgs[1],imgs[0]
|
626 |
+
im1 = np.array(imgs[0])
|
627 |
+
|
628 |
+
for i in range(len(imgs)-1):
|
629 |
+
im2 = np.array(imgs[i+1])
|
630 |
+
|
631 |
+
abs_diff = cv2.absdiff(im2 , im1)
|
632 |
+
|
633 |
+
abs_diff_t = cv2.threshold(abs_diff, int(thresh), 255, cv2.THRESH_BINARY)[1]
|
634 |
+
res = abs_diff_t.astype(np.uint8)
|
635 |
+
percentage = (np.count_nonzero(res) * 100)/ res.size
|
636 |
+
if "white" in diffcol: abs_diff = cv2.bitwise_not(abs_diff)
|
637 |
+
outnum.append(percentage)
|
638 |
+
|
639 |
+
abs_diff = Image.fromarray(abs_diff)
|
640 |
+
|
641 |
+
diffs.append(abs_diff)
|
642 |
+
|
643 |
+
outs = []
|
644 |
+
for i in range(len(ls)):
|
645 |
+
ls[i] = ls[i] + "\n Diff : " + str(round(outnum[i],3)) + "%"
|
646 |
+
|
647 |
+
if not revxy:
|
648 |
+
for diff,img in zip(diffs,imgs[1:]):
|
649 |
+
outs.append(diff)
|
650 |
+
outs.append(img)
|
651 |
+
outs.append(imgs[0])
|
652 |
+
ss = ["diff",ss[0],"source"]
|
653 |
+
return outs,ss,ls
|
654 |
+
else:
|
655 |
+
outs = [imgs[0]]*len(diffs) + imgs[1:]+ diffs
|
656 |
+
ss = ["source",ss[0],"diff"]
|
657 |
+
return outs,ls,ss
|
658 |
+
|
659 |
+
def lbw(lora,lwei,elemental):
|
660 |
+
elemental = elemental.split(",")
|
661 |
+
for key in lora.modules.keys():
|
662 |
+
ratio = 1
|
663 |
+
picked = False
|
664 |
+
errormodules = []
|
665 |
+
|
666 |
+
for i,block in enumerate(BLOCKS):
|
667 |
+
if block in key:
|
668 |
+
ratio = lwei[i]
|
669 |
+
picked = True
|
670 |
+
currentblock = i
|
671 |
+
|
672 |
+
if not picked:
|
673 |
+
errormodules.append(key)
|
674 |
+
|
675 |
+
if len(elemental) > 0:
|
676 |
+
skey = key + BLOCKID[currentblock]
|
677 |
+
for d in elemental:
|
678 |
+
if d.count(":") != 2 :continue
|
679 |
+
dbs,dws,dr = (hyphener(d.split(":")[0]),d.split(":")[1],d.split(":")[2])
|
680 |
+
dbs,dws = (dbs.split(" "), dws.split(" "))
|
681 |
+
dbn,dbs = (True,dbs[1:]) if dbs[0] == "NOT" else (False,dbs)
|
682 |
+
dwn,dws = (True,dws[1:]) if dws[0] == "NOT" else (False,dws)
|
683 |
+
flag = dbn
|
684 |
+
for db in dbs:
|
685 |
+
if db in skey:
|
686 |
+
flag = not dbn
|
687 |
+
if flag:flag = dwn
|
688 |
+
else:continue
|
689 |
+
for dw in dws:
|
690 |
+
if dw in skey:
|
691 |
+
flag = not dwn
|
692 |
+
if flag:
|
693 |
+
dr = float(dr)
|
694 |
+
if princ :print(dbs,dws,key,dr)
|
695 |
+
ratio = dr
|
696 |
+
|
697 |
+
ltype = type(lora.modules[key]).__name__
|
698 |
+
set = False
|
699 |
+
if ltype in LORAANDSOON.keys():
|
700 |
+
setattr(lora.modules[key],LORAANDSOON[ltype],torch.nn.Parameter(getattr(lora.modules[key],LORAANDSOON[ltype]) * ratio))
|
701 |
+
#print(ltype)
|
702 |
+
set = True
|
703 |
+
else:
|
704 |
+
if hasattr(lora.modules[key],"up_model"):
|
705 |
+
lora.modules[key].up_model.weight= torch.nn.Parameter(lora.modules[key].up_model.weight *ratio)
|
706 |
+
#print("LoRA using LoCON")
|
707 |
+
set = True
|
708 |
+
else:
|
709 |
+
lora.modules[key].up.weight= torch.nn.Parameter(lora.modules[key].up.weight *ratio)
|
710 |
+
#print("LoRA")
|
711 |
+
set = True
|
712 |
+
if not set :
|
713 |
+
print("unkwon LoRA")
|
714 |
+
|
715 |
+
lora.name = lora.name +"added_by_lora_block_weight"+ str(random.random())
|
716 |
+
if len(errormodules) > 0:
|
717 |
+
print(errormodules)
|
718 |
+
return lora
|
719 |
+
|
720 |
+
LORAANDSOON = {
|
721 |
+
"LoraHadaModule" : "w1a",
|
722 |
+
"LycoHadaModule" : "w1a",
|
723 |
+
"FullModule" : "weight",
|
724 |
+
"IA3Module" : "w",
|
725 |
+
"LoraKronModule" : "w1",
|
726 |
+
"LycoKronModule" : "w1",
|
727 |
+
}
|
728 |
+
|
729 |
+
def hyphener(t):
|
730 |
+
t = t.split(" ")
|
731 |
+
for i,e in enumerate(t):
|
732 |
+
if "-" in e:
|
733 |
+
e = e.split("-")
|
734 |
+
if BLOCKID.index(e[1]) > BLOCKID.index(e[0]):
|
735 |
+
t[i] = " ".join(BLOCKID[BLOCKID.index(e[0]):BLOCKID.index(e[1])+1])
|
736 |
+
else:
|
737 |
+
t[i] = " ".join(BLOCKID[BLOCKID.index(e[1]):BLOCKID.index(e[0])+1])
|
738 |
+
return " ".join(t)
|
739 |
+
|
740 |
+
ELEMPRESETS="\
|
741 |
+
ATTNDEEPON:IN05-OUT05:attn:1\n\n\
|
742 |
+
ATTNDEEPOFF:IN05-OUT05:attn:0\n\n\
|
743 |
+
PROJDEEPOFF:IN05-OUT05:proj:0\n\n\
|
744 |
+
XYZ:::1"
|
sd-webui-stablesr/.gitignore
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# meta
|
2 |
+
.vscode/
|
3 |
+
__pycache__/
|
4 |
+
.DS_Store
|
5 |
+
|
6 |
+
# settings
|
7 |
+
models/*.ckpt
|
sd-webui-stablesr/LICENSE
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
S-Lab License 1.0
|
2 |
+
|
3 |
+
Copyright 2022 S-Lab
|
4 |
+
|
5 |
+
Redistribution and use for non-commercial purpose in source and
|
6 |
+
binary forms, with or without modification, are permitted provided
|
7 |
+
that the following conditions are met:
|
8 |
+
|
9 |
+
1. Redistributions of source code must retain the above copyright
|
10 |
+
notice, this list of conditions and the following disclaimer.
|
11 |
+
|
12 |
+
2. Redistributions in binary form must reproduce the above copyright
|
13 |
+
notice, this list of conditions and the following disclaimer in
|
14 |
+
the documentation and/or other materials provided with the
|
15 |
+
distribution.
|
16 |
+
|
17 |
+
3. Neither the name of the copyright holder nor the names of its
|
18 |
+
contributors may be used to endorse or promote products derived
|
19 |
+
from this software without specific prior written permission.
|
20 |
+
|
21 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
22 |
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
23 |
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
24 |
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
25 |
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
26 |
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
27 |
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
28 |
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
29 |
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
30 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
31 |
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
32 |
+
|
33 |
+
In the event that redistribution and/or use for commercial purpose in
|
34 |
+
source or binary forms, with or without modification is required,
|
35 |
+
please contact the contributor(s) of the work.
|
sd-webui-stablesr/LICENSE2
ADDED
@@ -0,0 +1,437 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Attribution-NonCommercial-ShareAlike 4.0 International
|
2 |
+
|
3 |
+
=======================================================================
|
4 |
+
|
5 |
+
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
6 |
+
does not provide legal services or legal advice. Distribution of
|
7 |
+
Creative Commons public licenses does not create a lawyer-client or
|
8 |
+
other relationship. Creative Commons makes its licenses and related
|
9 |
+
information available on an "as-is" basis. Creative Commons gives no
|
10 |
+
warranties regarding its licenses, any material licensed under their
|
11 |
+
terms and conditions, or any related information. Creative Commons
|
12 |
+
disclaims all liability for damages resulting from their use to the
|
13 |
+
fullest extent possible.
|
14 |
+
|
15 |
+
Using Creative Commons Public Licenses
|
16 |
+
|
17 |
+
Creative Commons public licenses provide a standard set of terms and
|
18 |
+
conditions that creators and other rights holders may use to share
|
19 |
+
original works of authorship and other material subject to copyright
|
20 |
+
and certain other rights specified in the public license below. The
|
21 |
+
following considerations are for informational purposes only, are not
|
22 |
+
exhaustive, and do not form part of our licenses.
|
23 |
+
|
24 |
+
Considerations for licensors: Our public licenses are
|
25 |
+
intended for use by those authorized to give the public
|
26 |
+
permission to use material in ways otherwise restricted by
|
27 |
+
copyright and certain other rights. Our licenses are
|
28 |
+
irrevocable. Licensors should read and understand the terms
|
29 |
+
and conditions of the license they choose before applying it.
|
30 |
+
Licensors should also secure all rights necessary before
|
31 |
+
applying our licenses so that the public can reuse the
|
32 |
+
material as expected. Licensors should clearly mark any
|
33 |
+
material not subject to the license. This includes other CC-
|
34 |
+
licensed material, or material used under an exception or
|
35 |
+
limitation to copyright. More considerations for licensors:
|
36 |
+
wiki.creativecommons.org/Considerations_for_licensors
|
37 |
+
|
38 |
+
Considerations for the public: By using one of our public
|
39 |
+
licenses, a licensor grants the public permission to use the
|
40 |
+
licensed material under specified terms and conditions. If
|
41 |
+
the licensor's permission is not necessary for any reason--for
|
42 |
+
example, because of any applicable exception or limitation to
|
43 |
+
copyright--then that use is not regulated by the license. Our
|
44 |
+
licenses grant only permissions under copyright and certain
|
45 |
+
other rights that a licensor has authority to grant. Use of
|
46 |
+
the licensed material may still be restricted for other
|
47 |
+
reasons, including because others have copyright or other
|
48 |
+
rights in the material. A licensor may make special requests,
|
49 |
+
such as asking that all changes be marked or described.
|
50 |
+
Although not required by our licenses, you are encouraged to
|
51 |
+
respect those requests where reasonable. More considerations
|
52 |
+
for the public:
|
53 |
+
wiki.creativecommons.org/Considerations_for_licensees
|
54 |
+
|
55 |
+
=======================================================================
|
56 |
+
|
57 |
+
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
58 |
+
Public License
|
59 |
+
|
60 |
+
By exercising the Licensed Rights (defined below), You accept and agree
|
61 |
+
to be bound by the terms and conditions of this Creative Commons
|
62 |
+
Attribution-NonCommercial-ShareAlike 4.0 International Public License
|
63 |
+
("Public License"). To the extent this Public License may be
|
64 |
+
interpreted as a contract, You are granted the Licensed Rights in
|
65 |
+
consideration of Your acceptance of these terms and conditions, and the
|
66 |
+
Licensor grants You such rights in consideration of benefits the
|
67 |
+
Licensor receives from making the Licensed Material available under
|
68 |
+
these terms and conditions.
|
69 |
+
|
70 |
+
|
71 |
+
Section 1 -- Definitions.
|
72 |
+
|
73 |
+
a. Adapted Material means material subject to Copyright and Similar
|
74 |
+
Rights that is derived from or based upon the Licensed Material
|
75 |
+
and in which the Licensed Material is translated, altered,
|
76 |
+
arranged, transformed, or otherwise modified in a manner requiring
|
77 |
+
permission under the Copyright and Similar Rights held by the
|
78 |
+
Licensor. For purposes of this Public License, where the Licensed
|
79 |
+
Material is a musical work, performance, or sound recording,
|
80 |
+
Adapted Material is always produced where the Licensed Material is
|
81 |
+
synched in timed relation with a moving image.
|
82 |
+
|
83 |
+
b. Adapter's License means the license You apply to Your Copyright
|
84 |
+
and Similar Rights in Your contributions to Adapted Material in
|
85 |
+
accordance with the terms and conditions of this Public License.
|
86 |
+
|
87 |
+
c. BY-NC-SA Compatible License means a license listed at
|
88 |
+
creativecommons.org/compatiblelicenses, approved by Creative
|
89 |
+
Commons as essentially the equivalent of this Public License.
|
90 |
+
|
91 |
+
d. Copyright and Similar Rights means copyright and/or similar rights
|
92 |
+
closely related to copyright including, without limitation,
|
93 |
+
performance, broadcast, sound recording, and Sui Generis Database
|
94 |
+
Rights, without regard to how the rights are labeled or
|
95 |
+
categorized. For purposes of this Public License, the rights
|
96 |
+
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
97 |
+
Rights.
|
98 |
+
|
99 |
+
e. Effective Technological Measures means those measures that, in the
|
100 |
+
absence of proper authority, may not be circumvented under laws
|
101 |
+
fulfilling obligations under Article 11 of the WIPO Copyright
|
102 |
+
Treaty adopted on December 20, 1996, and/or similar international
|
103 |
+
agreements.
|
104 |
+
|
105 |
+
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
106 |
+
any other exception or limitation to Copyright and Similar Rights
|
107 |
+
that applies to Your use of the Licensed Material.
|
108 |
+
|
109 |
+
g. License Elements means the license attributes listed in the name
|
110 |
+
of a Creative Commons Public License. The License Elements of this
|
111 |
+
Public License are Attribution, NonCommercial, and ShareAlike.
|
112 |
+
|
113 |
+
h. Licensed Material means the artistic or literary work, database,
|
114 |
+
or other material to which the Licensor applied this Public
|
115 |
+
License.
|
116 |
+
|
117 |
+
i. Licensed Rights means the rights granted to You subject to the
|
118 |
+
terms and conditions of this Public License, which are limited to
|
119 |
+
all Copyright and Similar Rights that apply to Your use of the
|
120 |
+
Licensed Material and that the Licensor has authority to license.
|
121 |
+
|
122 |
+
j. Licensor means the individual(s) or entity(ies) granting rights
|
123 |
+
under this Public License.
|
124 |
+
|
125 |
+
k. NonCommercial means not primarily intended for or directed towards
|
126 |
+
commercial advantage or monetary compensation. For purposes of
|
127 |
+
this Public License, the exchange of the Licensed Material for
|
128 |
+
other material subject to Copyright and Similar Rights by digital
|
129 |
+
file-sharing or similar means is NonCommercial provided there is
|
130 |
+
no payment of monetary compensation in connection with the
|
131 |
+
exchange.
|
132 |
+
|
133 |
+
l. Share means to provide material to the public by any means or
|
134 |
+
process that requires permission under the Licensed Rights, such
|
135 |
+
as reproduction, public display, public performance, distribution,
|
136 |
+
dissemination, communication, or importation, and to make material
|
137 |
+
available to the public including in ways that members of the
|
138 |
+
public may access the material from a place and at a time
|
139 |
+
individually chosen by them.
|
140 |
+
|
141 |
+
m. Sui Generis Database Rights means rights other than copyright
|
142 |
+
resulting from Directive 96/9/EC of the European Parliament and of
|
143 |
+
the Council of 11 March 1996 on the legal protection of databases,
|
144 |
+
as amended and/or succeeded, as well as other essentially
|
145 |
+
equivalent rights anywhere in the world.
|
146 |
+
|
147 |
+
n. You means the individual or entity exercising the Licensed Rights
|
148 |
+
under this Public License. Your has a corresponding meaning.
|
149 |
+
|
150 |
+
|
151 |
+
Section 2 -- Scope.
|
152 |
+
|
153 |
+
a. License grant.
|
154 |
+
|
155 |
+
1. Subject to the terms and conditions of this Public License,
|
156 |
+
the Licensor hereby grants You a worldwide, royalty-free,
|
157 |
+
non-sublicensable, non-exclusive, irrevocable license to
|
158 |
+
exercise the Licensed Rights in the Licensed Material to:
|
159 |
+
|
160 |
+
a. reproduce and Share the Licensed Material, in whole or
|
161 |
+
in part, for NonCommercial purposes only; and
|
162 |
+
|
163 |
+
b. produce, reproduce, and Share Adapted Material for
|
164 |
+
NonCommercial purposes only.
|
165 |
+
|
166 |
+
2. Exceptions and Limitations. For the avoidance of doubt, where
|
167 |
+
Exceptions and Limitations apply to Your use, this Public
|
168 |
+
License does not apply, and You do not need to comply with
|
169 |
+
its terms and conditions.
|
170 |
+
|
171 |
+
3. Term. The term of this Public License is specified in Section
|
172 |
+
6(a).
|
173 |
+
|
174 |
+
4. Media and formats; technical modifications allowed. The
|
175 |
+
Licensor authorizes You to exercise the Licensed Rights in
|
176 |
+
all media and formats whether now known or hereafter created,
|
177 |
+
and to make technical modifications necessary to do so. The
|
178 |
+
Licensor waives and/or agrees not to assert any right or
|
179 |
+
authority to forbid You from making technical modifications
|
180 |
+
necessary to exercise the Licensed Rights, including
|
181 |
+
technical modifications necessary to circumvent Effective
|
182 |
+
Technological Measures. For purposes of this Public License,
|
183 |
+
simply making modifications authorized by this Section 2(a)
|
184 |
+
(4) never produces Adapted Material.
|
185 |
+
|
186 |
+
5. Downstream recipients.
|
187 |
+
|
188 |
+
a. Offer from the Licensor -- Licensed Material. Every
|
189 |
+
recipient of the Licensed Material automatically
|
190 |
+
receives an offer from the Licensor to exercise the
|
191 |
+
Licensed Rights under the terms and conditions of this
|
192 |
+
Public License.
|
193 |
+
|
194 |
+
b. Additional offer from the Licensor -- Adapted Material.
|
195 |
+
Every recipient of Adapted Material from You
|
196 |
+
automatically receives an offer from the Licensor to
|
197 |
+
exercise the Licensed Rights in the Adapted Material
|
198 |
+
under the conditions of the Adapter's License You apply.
|
199 |
+
|
200 |
+
c. No downstream restrictions. You may not offer or impose
|
201 |
+
any additional or different terms or conditions on, or
|
202 |
+
apply any Effective Technological Measures to, the
|
203 |
+
Licensed Material if doing so restricts exercise of the
|
204 |
+
Licensed Rights by any recipient of the Licensed
|
205 |
+
Material.
|
206 |
+
|
207 |
+
6. No endorsement. Nothing in this Public License constitutes or
|
208 |
+
may be construed as permission to assert or imply that You
|
209 |
+
are, or that Your use of the Licensed Material is, connected
|
210 |
+
with, or sponsored, endorsed, or granted official status by,
|
211 |
+
the Licensor or others designated to receive attribution as
|
212 |
+
provided in Section 3(a)(1)(A)(i).
|
213 |
+
|
214 |
+
b. Other rights.
|
215 |
+
|
216 |
+
1. Moral rights, such as the right of integrity, are not
|
217 |
+
licensed under this Public License, nor are publicity,
|
218 |
+
privacy, and/or other similar personality rights; however, to
|
219 |
+
the extent possible, the Licensor waives and/or agrees not to
|
220 |
+
assert any such rights held by the Licensor to the limited
|
221 |
+
extent necessary to allow You to exercise the Licensed
|
222 |
+
Rights, but not otherwise.
|
223 |
+
|
224 |
+
2. Patent and trademark rights are not licensed under this
|
225 |
+
Public License.
|
226 |
+
|
227 |
+
3. To the extent possible, the Licensor waives any right to
|
228 |
+
collect royalties from You for the exercise of the Licensed
|
229 |
+
Rights, whether directly or through a collecting society
|
230 |
+
under any voluntary or waivable statutory or compulsory
|
231 |
+
licensing scheme. In all other cases the Licensor expressly
|
232 |
+
reserves any right to collect such royalties, including when
|
233 |
+
the Licensed Material is used other than for NonCommercial
|
234 |
+
purposes.
|
235 |
+
|
236 |
+
|
237 |
+
Section 3 -- License Conditions.
|
238 |
+
|
239 |
+
Your exercise of the Licensed Rights is expressly made subject to the
|
240 |
+
following conditions.
|
241 |
+
|
242 |
+
a. Attribution.
|
243 |
+
|
244 |
+
1. If You Share the Licensed Material (including in modified
|
245 |
+
form), You must:
|
246 |
+
|
247 |
+
a. retain the following if it is supplied by the Licensor
|
248 |
+
with the Licensed Material:
|
249 |
+
|
250 |
+
i. identification of the creator(s) of the Licensed
|
251 |
+
Material and any others designated to receive
|
252 |
+
attribution, in any reasonable manner requested by
|
253 |
+
the Licensor (including by pseudonym if
|
254 |
+
designated);
|
255 |
+
|
256 |
+
ii. a copyright notice;
|
257 |
+
|
258 |
+
iii. a notice that refers to this Public License;
|
259 |
+
|
260 |
+
iv. a notice that refers to the disclaimer of
|
261 |
+
warranties;
|
262 |
+
|
263 |
+
v. a URI or hyperlink to the Licensed Material to the
|
264 |
+
extent reasonably practicable;
|
265 |
+
|
266 |
+
b. indicate if You modified the Licensed Material and
|
267 |
+
retain an indication of any previous modifications; and
|
268 |
+
|
269 |
+
c. indicate the Licensed Material is licensed under this
|
270 |
+
Public License, and include the text of, or the URI or
|
271 |
+
hyperlink to, this Public License.
|
272 |
+
|
273 |
+
2. You may satisfy the conditions in Section 3(a)(1) in any
|
274 |
+
reasonable manner based on the medium, means, and context in
|
275 |
+
which You Share the Licensed Material. For example, it may be
|
276 |
+
reasonable to satisfy the conditions by providing a URI or
|
277 |
+
hyperlink to a resource that includes the required
|
278 |
+
information.
|
279 |
+
3. If requested by the Licensor, You must remove any of the
|
280 |
+
information required by Section 3(a)(1)(A) to the extent
|
281 |
+
reasonably practicable.
|
282 |
+
|
283 |
+
b. ShareAlike.
|
284 |
+
|
285 |
+
In addition to the conditions in Section 3(a), if You Share
|
286 |
+
Adapted Material You produce, the following conditions also apply.
|
287 |
+
|
288 |
+
1. The Adapter's License You apply must be a Creative Commons
|
289 |
+
license with the same License Elements, this version or
|
290 |
+
later, or a BY-NC-SA Compatible License.
|
291 |
+
|
292 |
+
2. You must include the text of, or the URI or hyperlink to, the
|
293 |
+
Adapter's License You apply. You may satisfy this condition
|
294 |
+
in any reasonable manner based on the medium, means, and
|
295 |
+
context in which You Share Adapted Material.
|
296 |
+
|
297 |
+
3. You may not offer or impose any additional or different terms
|
298 |
+
or conditions on, or apply any Effective Technological
|
299 |
+
Measures to, Adapted Material that restrict exercise of the
|
300 |
+
rights granted under the Adapter's License You apply.
|
301 |
+
|
302 |
+
|
303 |
+
Section 4 -- Sui Generis Database Rights.
|
304 |
+
|
305 |
+
Where the Licensed Rights include Sui Generis Database Rights that
|
306 |
+
apply to Your use of the Licensed Material:
|
307 |
+
|
308 |
+
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
309 |
+
to extract, reuse, reproduce, and Share all or a substantial
|
310 |
+
portion of the contents of the database for NonCommercial purposes
|
311 |
+
only;
|
312 |
+
|
313 |
+
b. if You include all or a substantial portion of the database
|
314 |
+
contents in a database in which You have Sui Generis Database
|
315 |
+
Rights, then the database in which You have Sui Generis Database
|
316 |
+
Rights (but not its individual contents) is Adapted Material,
|
317 |
+
including for purposes of Section 3(b); and
|
318 |
+
|
319 |
+
c. You must comply with the conditions in Section 3(a) if You Share
|
320 |
+
all or a substantial portion of the contents of the database.
|
321 |
+
|
322 |
+
For the avoidance of doubt, this Section 4 supplements and does not
|
323 |
+
replace Your obligations under this Public License where the Licensed
|
324 |
+
Rights include other Copyright and Similar Rights.
|
325 |
+
|
326 |
+
|
327 |
+
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
328 |
+
|
329 |
+
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
330 |
+
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
331 |
+
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
332 |
+
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
333 |
+
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
334 |
+
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
335 |
+
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
336 |
+
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
337 |
+
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
338 |
+
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
339 |
+
|
340 |
+
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
341 |
+
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
342 |
+
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
343 |
+
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
344 |
+
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
345 |
+
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
346 |
+
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
347 |
+
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
348 |
+
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
349 |
+
|
350 |
+
c. The disclaimer of warranties and limitation of liability provided
|
351 |
+
above shall be interpreted in a manner that, to the extent
|
352 |
+
possible, most closely approximates an absolute disclaimer and
|
353 |
+
waiver of all liability.
|
354 |
+
|
355 |
+
|
356 |
+
Section 6 -- Term and Termination.
|
357 |
+
|
358 |
+
a. This Public License applies for the term of the Copyright and
|
359 |
+
Similar Rights licensed here. However, if You fail to comply with
|
360 |
+
this Public License, then Your rights under this Public License
|
361 |
+
terminate automatically.
|
362 |
+
|
363 |
+
b. Where Your right to use the Licensed Material has terminated under
|
364 |
+
Section 6(a), it reinstates:
|
365 |
+
|
366 |
+
1. automatically as of the date the violation is cured, provided
|
367 |
+
it is cured within 30 days of Your discovery of the
|
368 |
+
violation; or
|
369 |
+
|
370 |
+
2. upon express reinstatement by the Licensor.
|
371 |
+
|
372 |
+
For the avoidance of doubt, this Section 6(b) does not affect any
|
373 |
+
right the Licensor may have to seek remedies for Your violations
|
374 |
+
of this Public License.
|
375 |
+
|
376 |
+
c. For the avoidance of doubt, the Licensor may also offer the
|
377 |
+
Licensed Material under separate terms or conditions or stop
|
378 |
+
distributing the Licensed Material at any time; however, doing so
|
379 |
+
will not terminate this Public License.
|
380 |
+
|
381 |
+
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
382 |
+
License.
|
383 |
+
|
384 |
+
|
385 |
+
Section 7 -- Other Terms and Conditions.
|
386 |
+
|
387 |
+
a. The Licensor shall not be bound by any additional or different
|
388 |
+
terms or conditions communicated by You unless expressly agreed.
|
389 |
+
|
390 |
+
b. Any arrangements, understandings, or agreements regarding the
|
391 |
+
Licensed Material not stated herein are separate from and
|
392 |
+
independent of the terms and conditions of this Public License.
|
393 |
+
|
394 |
+
|
395 |
+
Section 8 -- Interpretation.
|
396 |
+
|
397 |
+
a. For the avoidance of doubt, this Public License does not, and
|
398 |
+
shall not be interpreted to, reduce, limit, restrict, or impose
|
399 |
+
conditions on any use of the Licensed Material that could lawfully
|
400 |
+
be made without permission under this Public License.
|
401 |
+
|
402 |
+
b. To the extent possible, if any provision of this Public License is
|
403 |
+
deemed unenforceable, it shall be automatically reformed to the
|
404 |
+
minimum extent necessary to make it enforceable. If the provision
|
405 |
+
cannot be reformed, it shall be severed from this Public License
|
406 |
+
without affecting the enforceability of the remaining terms and
|
407 |
+
conditions.
|
408 |
+
|
409 |
+
c. No term or condition of this Public License will be waived and no
|
410 |
+
failure to comply consented to unless expressly agreed to by the
|
411 |
+
Licensor.
|
412 |
+
|
413 |
+
d. Nothing in this Public License constitutes or may be interpreted
|
414 |
+
as a limitation upon, or waiver of, any privileges and immunities
|
415 |
+
that apply to the Licensor or You, including from the legal
|
416 |
+
processes of any jurisdiction or authority.
|
417 |
+
|
418 |
+
=======================================================================
|
419 |
+
|
420 |
+
Creative Commons is not a party to its public
|
421 |
+
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
422 |
+
its public licenses to material it publishes and in those instances
|
423 |
+
will be considered the “Licensor.” The text of the Creative Commons
|
424 |
+
public licenses is dedicated to the public domain under the CC0 Public
|
425 |
+
Domain Dedication. Except for the limited purpose of indicating that
|
426 |
+
material is shared under a Creative Commons public license or as
|
427 |
+
otherwise permitted by the Creative Commons policies published at
|
428 |
+
creativecommons.org/policies, Creative Commons does not authorize the
|
429 |
+
use of the trademark "Creative Commons" or any other trademark or logo
|
430 |
+
of Creative Commons without its prior written consent including,
|
431 |
+
without limitation, in connection with any unauthorized modifications
|
432 |
+
to any of its public licenses or any other arrangements,
|
433 |
+
understandings, or agreements concerning use of licensed material. For
|
434 |
+
the avoidance of doubt, this paragraph does not form part of the
|
435 |
+
public licenses.
|
436 |
+
|
437 |
+
Creative Commons may be contacted at creativecommons.org.
|
sd-webui-stablesr/README.md
ADDED
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# StableSR for Stable Diffusion WebUI
|
2 |
+
|
3 |
+
Licensed under S-Lab License 1.0
|
4 |
+
|
5 |
+
[![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa]
|
6 |
+
|
7 |
+
English|[中文](README_CN.md)
|
8 |
+
|
9 |
+
- StableSR is a competitive super-resolution method originally proposed by Jianyi Wang et al.
|
10 |
+
- This repository is a migration of the StableSR project to the Automatic1111 WebUI.
|
11 |
+
|
12 |
+
Relevant Links
|
13 |
+
|
14 |
+
> Click to view high-quality official examples!
|
15 |
+
|
16 |
+
- [Project Page](https://iceclear.github.io/projects/stablesr/)
|
17 |
+
- [Official Repository](https://github.com/IceClear/StableSR)
|
18 |
+
- [Paper on arXiv](https://arxiv.org/abs/2305.07015)
|
19 |
+
|
20 |
+
> If you find this project useful, please give me & Jianyi Wang a star! ⭐
|
21 |
+
|
22 |
+
***
|
23 |
+
|
24 |
+
## Features
|
25 |
+
|
26 |
+
1. **High-fidelity detailed image upscaling**:
|
27 |
+
- Being very detailed while keeping the face identity of your characters.
|
28 |
+
- Suitable for most images (Realistic or Anime, Photography or AIGC, SD 1.5 or Midjourney images...) [Official Examples](https://iceclear.github.io/projects/stablesr/)
|
29 |
+
2. **Less VRAM consumption**
|
30 |
+
- I remove the VRAM-expensive modules in the official implementation.
|
31 |
+
- The remaining model is much smaller than ControlNet Tile model and requires less VRAM.
|
32 |
+
- When combined with Tiled Diffusion & VAE, you can do 4k image super-resolution with limited VRAM (e.g., < 12 GB).
|
33 |
+
> Please be aware that sdp may lead to OOM for some unknown reasons. You may use xformers instead.
|
34 |
+
3. **Wavelet Color Fix**
|
35 |
+
- The official StableSR will significantly change the color of the generated image. The problem will be even more prominent when upscaling in tiles.
|
36 |
+
- I implement a powerful post-processing technique that effectively matches the color of the upscaled image to the original. See [Wavelet Color Fix Example](https://imgsli.com/MTgwNDg2/).
|
37 |
+
|
38 |
+
***
|
39 |
+
|
40 |
+
## Usage
|
41 |
+
|
42 |
+
### 1. Installation
|
43 |
+
|
44 |
+
⚪ Method 1: Official Market
|
45 |
+
|
46 |
+
- Open Automatic1111 WebUI -> Click Tab "Extensions" -> Click Tab "Available" -> Find "StableSR" -> Click "Install"
|
47 |
+
|
48 |
+
⚪ Method 2: URL Install
|
49 |
+
|
50 |
+
- Open Automatic1111 WebUI -> Click Tab "Extensions" -> Click Tab "Install from URL" -> type in https://github.com/pkuliyi2015/sd-webui-stablesr.git -> Click "Install"
|
51 |
+
|
52 |
+
![installation](https://github.com/pkuliyi2015/multidiffusion-img-demo/blob/master/installation.png?raw=true)
|
53 |
+
|
54 |
+
### 2. Download the main components
|
55 |
+
|
56 |
+
- You MUST use the Stable Diffusion V2.1 512 **EMA** checkpoint (~5.21GB) from StabilityAI
|
57 |
+
- You can download it from [HuggingFace](https://huggingface.co/stabilityai/stable-diffusion-2-1-base)
|
58 |
+
- Put into stable-diffusion-webui/models/Stable-Diffusion/
|
59 |
+
|
60 |
+
> While it requires a SD2.1 checkpoint, you can still upscale ANY image (even from SD1.5 or NSFW). Your image won't be censored and the output quality won't be affected.
|
61 |
+
|
62 |
+
- Download the extracted StableSR module
|
63 |
+
- Official resources: [HuggingFace](https://huggingface.co/Iceclear/StableSR/resolve/main/weibu_models.zip) (~1.2 G). Note that this is a zip file containing both the StableSR module and the VQVAE.
|
64 |
+
- My resources: <[GoogleDrive](https://drive.google.com/file/d/1tWjkZQhfj07sHDR4r9Ta5Fk4iMp1t3Qw/view?usp=sharing)> <[百度网盘-提取码aguq](https://pan.baidu.com/s/1Nq_6ciGgKnTu0W14QcKKWg?pwd=aguq)>
|
65 |
+
- Put the StableSR module (~400MB) into your stable-diffusion-webui/extensions/sd-webui-stablesr/models/
|
66 |
+
|
67 |
+
### 3. Optional components
|
68 |
+
|
69 |
+
- Install [Tiled Diffusion & VAE]((https://github.com/pkuliyi2015/multidiffusion-upscaler-for-automatic1111)) extension
|
70 |
+
- The original StableSR easily gets OOM for large images > 512.
|
71 |
+
- For better quality and less VRAM usage, we recommend Tiled Diffusion & VAE.
|
72 |
+
- Use the Official VQGAN VAE
|
73 |
+
- Official resources: See the link in 2.
|
74 |
+
- My resources: <[GoogleDrive](https://drive.google.com/file/d/1ARtDMia3_CbwNsGxxGcZ5UP75W4PeIEI/view?usp=share_link)> <[百度网盘-提取码83u9](https://pan.baidu.com/s/1YCYmGBethR9JZ8-eypoIiQ?pwd=83u9)>
|
75 |
+
- Put the VQVAE (~700MB) into your stable-diffusion-webui/models/VAE
|
76 |
+
|
77 |
+
### 4. Extension Usage
|
78 |
+
|
79 |
+
- At the top of the WebUI, select the v2-1_512-ema-pruned checkpoint you downloaded.
|
80 |
+
- Switch to img2img tag. Find the "Scripts" dropdown at the bottom of the page.
|
81 |
+
- Select the StableSR script.
|
82 |
+
- Click the refresh button and select the StableSR checkpoint you have downloaded.
|
83 |
+
- Choose a scale factor.
|
84 |
+
- Upload your image and start generation (can work without prompts).
|
85 |
+
- Euler a sampler is recommended. CFG Scale<=2, Steps >= 20.
|
86 |
+
- For output image size > 512, we recommend using Tiled Diffusion & VAE, otherwise, the image quality may not be ideal, and the VRAM usage will be huge.
|
87 |
+
- Here are the official Tiled Diffusion settings:
|
88 |
+
- Method = Mixture of Diffusers
|
89 |
+
- Latent tile size = 64, Latent tile overlap = 32
|
90 |
+
- Latent tile batch size as large as possible before Out of Memory.
|
91 |
+
- Upscaler MUST be None (will not upscale here; instead, upscale in StableSR).
|
92 |
+
- The following figure shows the recommended settings for 24GB VRAM.
|
93 |
+
- For a 6GB device, **just change Tiled Diffusion Latent tile batch size to 1, Tiled VAE Encoder Tile Size to 1024, Decoder Tile Size to 128.**
|
94 |
+
- SDP attention optimization may lead to OOM. Please use xformers in that case.
|
95 |
+
- You DON'T need to change other settings in Tiled Diffusion & Tiled VAE unless you have a very deep understanding. **These params are almost optimal for StableSR.**
|
96 |
+
![recommended settings](https://github.com/pkuliyi2015/multidiffusion-img-demo/blob/master/recommended_settings_24GB.jpg?raw=true)
|
97 |
+
|
98 |
+
|
99 |
+
|
100 |
+
### 5. Options Explained
|
101 |
+
|
102 |
+
- What is "Pure Noise"?
|
103 |
+
- Pure Noise refers to starting from a fully random noise tensor instead of your image. **This is the default behavior in the StableSR paper.**
|
104 |
+
- When enabling it, the script ignores your denoising strength and gives you much more detailed images, but also changes the color & sharpness significantly
|
105 |
+
- When disabling it, the script starts by adding some noise to your image. The result will be not fully detailed, even if you set denoising strength = 1 (but maybe aesthetically good). See [Comparison](https://imgsli.com/MTgwMTMx).
|
106 |
+
- If you disable Pure Noise, we recommend denoising strength=1
|
107 |
+
- What is "Color Fix"?
|
108 |
+
- This is to mitigate the color shift problem from StableSR and the tiling process.
|
109 |
+
- AdaIN simply adjusts the color statistics between the original and the outcome images. This is the official algorithm but ineffective in many cases.
|
110 |
+
- Wavelet decomposes the original and the outcome images into low and high frequency, and then replace the outcome image's low-frequency part (colors) with the original image's. This is very powerful for uneven color shifting. The algorithm is from GIMP and Krita, which will take several seconds for each image.
|
111 |
+
- When enabling color fix, the original image will also show up in your preview window, but will NOT be saved automatically.
|
112 |
+
|
113 |
+
### 6. Important Notice
|
114 |
+
|
115 |
+
> Why my results are different from the offical examples?
|
116 |
+
|
117 |
+
- It is not your or our fault.
|
118 |
+
- This extension has the same UNet model weights as the StableSR if installed correctly.
|
119 |
+
- If you install the optional VQVAE, the whole model weights will be the same as the official model with fusion weights=0.
|
120 |
+
- However, your result will be **not as good as** the official results, because:
|
121 |
+
- Sampler Difference:
|
122 |
+
- The official repo does 100 or 200 steps of legacy DDPM sampling with a custom timestep scheduler, and samples without negative prompts.
|
123 |
+
- However, WebUI doesn't offer such a sampler, and it must sample with negative prompts. **This is the main difference.**
|
124 |
+
- VQVAE Decoder Difference:
|
125 |
+
- The official VQVAE Decoder takes some Encoder features as input.
|
126 |
+
- However, in practice, I found these features are astonishingly huge for large images. (>10G for 4k images even in float16!)
|
127 |
+
- Hence, **I removed the CFW component in VAE Decoder**. As this lead to inferior fidelity in details, I will try to add it back later as an option.
|
128 |
+
|
129 |
+
***
|
130 |
+
## License
|
131 |
+
|
132 |
+
This project is licensed under:
|
133 |
+
|
134 |
+
- S-Lab License 1.0.
|
135 |
+
- [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License][cc-by-nc-sa], due to the use of the NVIDIA SPADE module.
|
136 |
+
|
137 |
+
[![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa]
|
138 |
+
|
139 |
+
[cc-by-nc-sa]: http://creativecommons.org/licenses/by-nc-sa/4.0/
|
140 |
+
[cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png
|
141 |
+
[cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg
|
142 |
+
|
143 |
+
### Disclaimer
|
144 |
+
|
145 |
+
- All code in this extension is for research purposes only.
|
146 |
+
- The commercial use of the code and checkpoint is **strictly prohibited**.
|
147 |
+
|
148 |
+
### Important Notice for Outcome Images
|
149 |
+
|
150 |
+
- Please note that the CC BY-NC-SA 4.0 license in the NVIDIA SPADE module also prohibits the commercial use of outcome images.
|
151 |
+
- Jianyi Wang may change the SPADE module to a commercial-friendly one but he is busy.
|
152 |
+
- If you wish to *speed up* his process for commercial purposes, please contact him through email: [email protected]
|
153 |
+
|
154 |
+
## Acknowledgments
|
155 |
+
|
156 |
+
I would like to thank Jianyi Wang et al. for the original StableSR method.
|
sd-webui-stablesr/README_CN.md
ADDED
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# StableSR - Stable Diffusion WebUI
|
2 |
+
|
3 |
+
Licensed under S-Lab License 1.0
|
4 |
+
|
5 |
+
[![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa]
|
6 |
+
|
7 |
+
[English](README.md) | 中文
|
8 |
+
|
9 |
+
- StableSR 是由 Jianyi Wang 等人提出的强力超分辨率项目。
|
10 |
+
- 本仓库将 StableSR 项目迁移到 Automatic1111 WebUI。
|
11 |
+
|
12 |
+
相关链接
|
13 |
+
|
14 |
+
> 点击查看大量官方示例!
|
15 |
+
|
16 |
+
- [项目页面](https://iceclear.github.io/projects/stablesr/)
|
17 |
+
- [官方仓库](https://github.com/IceClear/StableSR)
|
18 |
+
- [论文](https://arxiv.org/abs/2305.07015)
|
19 |
+
|
20 |
+
> 如果你觉得这个项目有帮助,请给我和 Jianyi Wang 的仓库点个星!⭐
|
21 |
+
***
|
22 |
+
|
23 |
+
## 功能
|
24 |
+
|
25 |
+
1. **高保真图像放大**:
|
26 |
+
- 不修改人物脸部的同时添加非常细致的细节和纹理
|
27 |
+
- 适合大多数图片(真实或动漫,摄影作品或AIGC,SD 1.5或Midjourney图片...)
|
28 |
+
2. **较少的显存消耗**:
|
29 |
+
- 我移除了官方实现中显存消耗高的模块。
|
30 |
+
- 剩下的模型比ControlNet Tile模型小得多,需要的显存也少得多。
|
31 |
+
- 当结合Tiled Diffusion & VAE时,你可以在有限的显存(例如,<12GB)中进行4k图像放大。
|
32 |
+
> 注意,sdp可能会不明原因炸显存。建议使用xformers。
|
33 |
+
3. **小波分解颜色修正**:
|
34 |
+
- StableSR官方实现有明显的颜色偏移,这一问题在分块放大时更加明显。
|
35 |
+
- 我实现了一个强大的后处理技术,有效地匹配放大图像与原图的颜色。请看[小波分解颜色修正例子](https://imgsli.com/MTgwNDg2/)。
|
36 |
+
|
37 |
+
***
|
38 |
+
## 使用
|
39 |
+
|
40 |
+
### 1. 安装
|
41 |
+
|
42 |
+
⚪ 方法 1: 官方市场
|
43 |
+
|
44 |
+
- 打开Automatic1111 WebUI -> 点击“扩展”选项卡 -> 点击“可用”选项卡 -> 找到“StableSR” -> 点击“安装”
|
45 |
+
|
46 |
+
⚪ 方法 2: URL 安装
|
47 |
+
|
48 |
+
- 打开 Automatic1111 WebUI -> 点击 "Extensions" 标签页 -> 点击 "Install from URL" 标签页 -> 输入 https://github.com/pkuliyi2015/sd-webui-stablesr.git -> 点击 "Install"
|
49 |
+
|
50 |
+
![installation](https://github.com/pkuliyi2015/multidiffusion-img-demo/blob/master/installation.png?raw=true)
|
51 |
+
|
52 |
+
### 2. 必须模型
|
53 |
+
|
54 |
+
- 你必须使用 StabilityAI 官方的 Stable Diffusion V2.1 512 **EMA** 模型(约 5.21GB)
|
55 |
+
- 你可以从 [HuggingFace](https://huggingface.co/stabilityai/stable-diffusion-2-1-base) 下载
|
56 |
+
- 放入 stable-diffusion-webui/models/Stable-Diffusion/ 文件夹
|
57 |
+
> 虽然StableSR需要一个SD2.1的模型权重,但你仍然可以放大来自SD1.5的图片。NSFW图片不会被模型扭曲,输出质量也不会受到影响。
|
58 |
+
- 下载 StableSR 模块
|
59 |
+
- 官方资源:[HuggingFace](https://huggingface.co/Iceclear/StableSR/resolve/main/weibu_models.zip) (约1.2G)。请注意这是一个zip文件,同时包含StableSR模块和可选组件VQVAE.
|
60 |
+
- 我的资源:<[GoogleDrive](https://drive.google.com/file/d/1tWjkZQhfj07sHDR4r9Ta5Fk4iMp1t3Qw/view?usp=sharing)> <[百度网盘-提取码aguq](https://pan.baidu.com/s/1Nq_6ciGgKnTu0W14QcKKWg?pwd=aguq)>
|
61 |
+
- 把StableSR模块(约400M大小)放入 stable-diffusion-webui/extensions/sd-webui-stablesr/models/ 文件夹
|
62 |
+
|
63 |
+
### 3. 可选组件
|
64 |
+
|
65 |
+
- 安装 [Tiled Diffusion & VAE](https://github.com/pkuliyi2015/multidiffusion-upscaler-for-automatic1111) 扩展
|
66 |
+
- 原始的 StableSR 对大于 512 的大图像容易出现 OOM。
|
67 |
+
- 为了获得更好的质量和更少的 VRAM 使用,我们建议使用 Tiled Diffusion & VAE。
|
68 |
+
- 使用官方 VQGAN VAE
|
69 |
+
- 官方资源:同2中的链接
|
70 |
+
- 我的资源:<[GoogleDrive](https://drive.google.com/file/d/1ARtDMia3_CbwNsGxxGcZ5UP75W4PeIEI/view?usp=share_link)> <[百度网盘-提取码83u9](https://pan.baidu.com/s/1YCYmGBethR9JZ8-eypoIiQ?pwd=83u9)>
|
71 |
+
- 把VQVAE(约750MB大小)放在你的 stable-diffusion-webui/models/VAE 中
|
72 |
+
|
73 |
+
### 4. 扩展使用
|
74 |
+
|
75 |
+
- 在 WebUI 的顶部,选择你下载的 v2-1_512-ema-pruned 模型。
|
76 |
+
- 切换到 img2img 标签。在页面底部找到 "Scripts" 下拉列表。
|
77 |
+
- 选择 StableSR 脚本。
|
78 |
+
- 点击刷新按钮,选择你已下载的 StableSR 检查点。
|
79 |
+
- 选择一个放大因子。
|
80 |
+
- 上传你的图像并开始生成(无需提示也能工作)。
|
81 |
+
- 推荐使用 Euler a 采样器,CFG值<=2,步数 >= 20。
|
82 |
+
- 如果生成图像尺寸 > 512,我们推荐使用 Tiled Diffusion & VAE,否则,图像质量可能不理想,VRAM 使用量也会很大。
|
83 |
+
- 这里是官方推荐的 Tiled Diffusion 设置。
|
84 |
+
- 方法 = Mixture of Diffusers
|
85 |
+
- 隐空间Tile大小 = 64,隐空间Tile重叠 = 32
|
86 |
+
- Tile批大小尽可能大,直到差一点点就炸显存为止。
|
87 |
+
- Upscaler**必须**选择None。
|
88 |
+
- 下图是24GB显存的推荐设置。
|
89 |
+
- 对于4GB的设备,**只需将Tiled Diffusion Latent tile批处理大小改为1,Tiled VAE编码器Tile大小改为1024,解码器Tile大小改为128。**
|
90 |
+
- SDP注意力优化可能会导致OOM(内存不足),因此推荐使用xformers。
|
91 |
+
- 除非你有深入的理解,否则你**不要**改变Tiled Diffusion & Tiled VAE中的其他设置。**这些参数对于StableSR基本上是最优��。**
|
92 |
+
![推荐设置](https://github.com/pkuliyi2015/multidiffusion-img-demo/blob/master/recommended_settings_24GB.jpg?raw=true)
|
93 |
+
|
94 |
+
### 5. 参数解释
|
95 |
+
|
96 |
+
- 什么是 "Pure Noise"?
|
97 |
+
- Pure Noise也就是纯噪声,指的是从完全随机的噪声张量开始,而不是从你的图像开始。**这是 StableSR 论文中的默认做法。**
|
98 |
+
- 启用这个选项时,脚本会忽略你的重绘幅度设置。产出将会是更详细的图像,但也会显著改变颜色和锐度。
|
99 |
+
- 禁用这个选项时,脚本会开始添加一些噪声到你的图像。即使你将去噪强度设为1,结果也不会那么的细节(但可能更和谐好看)。参见 [对比图](https://imgsli.com/MTgwMTMx)。
|
100 |
+
- 如果禁用Pure Noise,推荐重绘幅度设置为1
|
101 |
+
- 什么是"颜色修正"?
|
102 |
+
- 这是为了缓解来自StableSR和Tile处理过程中的颜色偏移问题。
|
103 |
+
- AdaIN简单地匹配原图和结果图的颜色统计信息。这是StableSR官方算法,但常常效果不佳。
|
104 |
+
- Wavelet将原图和结果图分解为低频和高频,然后用原图的低频信息(颜色)替换掉结果图的低频信息。该算法对于不均匀的颜色偏移非常强力。算法来自GIMP和Krita,对每张图像需要几秒钟的时间。
|
105 |
+
- 启用颜色修正时,原图也会出现在您的预览窗口中,但不会被自动保存。
|
106 |
+
|
107 |
+
### 6. 重要问题
|
108 |
+
|
109 |
+
> 为什么我的结果和官方示例不同?
|
110 |
+
|
111 |
+
- 这不是你或我们的错。
|
112 |
+
- 如果正确安装,这个扩展有与 StableSR 相同的 UNet 模型权重。
|
113 |
+
- 如果你安装了可选的 VQVAE,整个模型权重将与融合权重为 0 的官方模型相同。
|
114 |
+
- 但是,你的结果将**不如**官方结果,因为:
|
115 |
+
- 采样器差异:
|
116 |
+
- 官方仓库进行 100 或 200 步的 legacy DDPM 采样,并使用自定义的时间步调度器,采样时不使用负提示。
|
117 |
+
- 然而,WebUI 不提供这样的采样器,必须带有负提示进行采样。**这是主要的差异。**
|
118 |
+
- VQVAE 解码器差异:
|
119 |
+
- 官方 VQVAE 解码器将一些编码器特征作为输入。
|
120 |
+
- 然而,在实践中,我发现这些特征对于大图像来说非常大。 (>10G 用于 4k 图像,即使是在 float16!)
|
121 |
+
- 因此,**我移除了 VAE 解码器中的 CFW 组件**。由于这导致了对细节的较低保真度,我将尝试将它作为一个选项添加回去。
|
122 |
+
|
123 |
+
***
|
124 |
+
## 许可
|
125 |
+
|
126 |
+
此项目在以下许可下授权:
|
127 |
+
|
128 |
+
- S-Lab License 1.0.
|
129 |
+
- [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License][cc-by-nc-sa],由于使用了 NVIDIA SPADE 模块。
|
130 |
+
|
131 |
+
[![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa]
|
132 |
+
|
133 |
+
[cc-by-nc-sa]: http://creativecommons.org/licenses/by-nc-sa/4.0/
|
134 |
+
[cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png
|
135 |
+
[cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg
|
136 |
+
|
137 |
+
### 免责声明
|
138 |
+
|
139 |
+
- 此扩展中的所有代码仅供研究目的。
|
140 |
+
- 严禁贩售代码和权重
|
141 |
+
|
142 |
+
### 产出图像的重要通知
|
143 |
+
|
144 |
+
- 请注意,NVIDIA SPADE 模块中的 CC BY-NC-SA 4.0 许可也禁止把产生的图像用于商业用途。
|
145 |
+
- Jianyi Wang 可能会将 SPADE 模块更改为商业友好的一个,但他很忙。
|
146 |
+
- 如果你希望**加快**他的进度,请通过电子邮件与他联系:[email protected]
|
147 |
+
|
148 |
+
## 致谢
|
149 |
+
|
150 |
+
感谢 Jianyi Wang 等人提出的 StableSR 方法
|
sd-webui-stablesr/scripts/__pycache__/stablesr.cpython-310.pyc
ADDED
Binary file (10.2 kB). View file
|
|
sd-webui-stablesr/scripts/stablesr.py
ADDED
@@ -0,0 +1,276 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
'''
|
2 |
+
# --------------------------------------------------------------------------------
|
3 |
+
#
|
4 |
+
# StableSR for Automatic1111 WebUI
|
5 |
+
#
|
6 |
+
# Introducing state-of-the super-resolution method: StableSR!
|
7 |
+
# Techniques is originally proposed by my schoolmate Jianyi Wang et, al.
|
8 |
+
#
|
9 |
+
# Project Page: https://iceclear.github.io/projects/stablesr/
|
10 |
+
# Official Repo: https://github.com/IceClear/StableSR
|
11 |
+
# Paper: https://arxiv.org/abs/2305.07015
|
12 |
+
#
|
13 |
+
# @original author: Jianyi Wang et, al.
|
14 |
+
# @migration: LI YI
|
15 |
+
# @organization: Nanyang Technological University - Singapore
|
16 |
+
# @date: 2023-05-20
|
17 |
+
# @license:
|
18 |
+
# S-Lab License 1.0 (see LICENSE file)
|
19 |
+
# CC BY-NC-SA 4.0 (required by NVIDIA SPADE module)
|
20 |
+
#
|
21 |
+
# @disclaimer:
|
22 |
+
# All code in this extension is for research purpose only.
|
23 |
+
# The commercial use of the code & checkpoint is strictly prohibited.
|
24 |
+
#
|
25 |
+
# --------------------------------------------------------------------------------
|
26 |
+
#
|
27 |
+
# IMPORTANT NOTICE FOR OUTCOME IMAGES:
|
28 |
+
# - Please be aware that the CC BY-NC-SA 4.0 license in SPADE module
|
29 |
+
# also prohibits the commercial use of outcome images.
|
30 |
+
# - Jianyi Wang may change the SPADE module to a commercial-friendly one.
|
31 |
+
# If you want to use the outcome images for commercial purposes, please
|
32 |
+
# contact Jianyi Wang for more information.
|
33 |
+
#
|
34 |
+
# Please give me a star (and also Jianyi's repo) if you like this project!
|
35 |
+
#
|
36 |
+
# --------------------------------------------------------------------------------
|
37 |
+
'''
|
38 |
+
|
39 |
+
import os
|
40 |
+
import torch
|
41 |
+
import gradio as gr
|
42 |
+
import numpy as np
|
43 |
+
import PIL.Image as Image
|
44 |
+
|
45 |
+
from pathlib import Path
|
46 |
+
from torch import Tensor
|
47 |
+
from tqdm import tqdm
|
48 |
+
|
49 |
+
from modules import scripts, processing, sd_samplers, devices, images, shared
|
50 |
+
from modules.processing import StableDiffusionProcessingImg2Img, Processed
|
51 |
+
from modules.shared import opts
|
52 |
+
from ldm.modules.diffusionmodules.openaimodel import UNetModel
|
53 |
+
|
54 |
+
from srmodule.spade import SPADELayers
|
55 |
+
from srmodule.struct_cond import EncoderUNetModelWT, build_unetwt
|
56 |
+
from srmodule.colorfix import adain_color_fix, wavelet_color_fix
|
57 |
+
|
58 |
+
SD_WEBUI_PATH = Path.cwd()
|
59 |
+
ME_PATH = SD_WEBUI_PATH / 'extensions' / 'sd-webui-stablesr'
|
60 |
+
MODEL_PATH = ME_PATH / 'models'
|
61 |
+
FORWARD_CACHE_NAME = 'org_forward_stablesr'
|
62 |
+
|
63 |
+
class StableSR:
|
64 |
+
def __init__(self, path, dtype, device):
|
65 |
+
state_dict = torch.load(path, map_location='cpu')
|
66 |
+
self.struct_cond_model: EncoderUNetModelWT = build_unetwt()
|
67 |
+
self.spade_layers: SPADELayers = SPADELayers()
|
68 |
+
self.struct_cond_model.load_from_dict(state_dict)
|
69 |
+
self.spade_layers.load_from_dict(state_dict)
|
70 |
+
del state_dict
|
71 |
+
self.struct_cond_model.apply(lambda x: x.to(dtype=dtype, device=device))
|
72 |
+
self.spade_layers.apply(lambda x: x.to(dtype=dtype, device=device))
|
73 |
+
|
74 |
+
self.latent_image: Tensor = None
|
75 |
+
self.set_image_hooks = {}
|
76 |
+
self.struct_cond: Tensor = None
|
77 |
+
|
78 |
+
def set_latent_image(self, latent_image):
|
79 |
+
self.latent_image = latent_image
|
80 |
+
for hook in self.set_image_hooks.values():
|
81 |
+
hook(latent_image)
|
82 |
+
|
83 |
+
def hook(self, unet: UNetModel):
|
84 |
+
# hook unet to set the struct_cond
|
85 |
+
if not hasattr(unet, FORWARD_CACHE_NAME):
|
86 |
+
setattr(unet, FORWARD_CACHE_NAME, unet.forward)
|
87 |
+
|
88 |
+
def unet_forward(x, timesteps=None, context=None, y=None,**kwargs):
|
89 |
+
self.latent_image = self.latent_image.to(x.device)
|
90 |
+
# Ensure the device of all modules layers is the same as the unet
|
91 |
+
# This will fix the issue when user use --medvram or --lowvram
|
92 |
+
self.spade_layers.to(x.device)
|
93 |
+
self.struct_cond_model.to(x.device)
|
94 |
+
timesteps = timesteps.to(x.device)
|
95 |
+
self.struct_cond = None # mitigate vram peak
|
96 |
+
self.struct_cond = self.struct_cond_model(self.latent_image, timesteps[:self.latent_image.shape[0]])
|
97 |
+
return getattr(unet, FORWARD_CACHE_NAME)(x, timesteps, context, y, **kwargs)
|
98 |
+
|
99 |
+
unet.forward = unet_forward
|
100 |
+
|
101 |
+
self.spade_layers.hook(unet, lambda: self.struct_cond)
|
102 |
+
|
103 |
+
|
104 |
+
def unhook(self, unet: UNetModel):
|
105 |
+
# clean up cache
|
106 |
+
self.latent_image = None
|
107 |
+
self.struct_cond = None
|
108 |
+
self.set_image_hooks = {}
|
109 |
+
# unhook unet forward
|
110 |
+
if hasattr(unet, FORWARD_CACHE_NAME):
|
111 |
+
unet.forward = getattr(unet, FORWARD_CACHE_NAME)
|
112 |
+
delattr(unet, FORWARD_CACHE_NAME)
|
113 |
+
|
114 |
+
# unhook spade layers
|
115 |
+
self.spade_layers.unhook()
|
116 |
+
|
117 |
+
|
118 |
+
class Script(scripts.Script):
|
119 |
+
def __init__(self) -> None:
|
120 |
+
self.model_list = {}
|
121 |
+
self.load_model_list()
|
122 |
+
self.last_path = None
|
123 |
+
self.stablesr_model: StableSR = None
|
124 |
+
|
125 |
+
def load_model_list(self):
|
126 |
+
# traverse the CFG_PATH and add all files to the model list
|
127 |
+
self.model_list = {}
|
128 |
+
if not MODEL_PATH.exists():
|
129 |
+
MODEL_PATH.mkdir()
|
130 |
+
for file in MODEL_PATH.iterdir():
|
131 |
+
if file.is_file():
|
132 |
+
# save tha absolute path
|
133 |
+
self.model_list[file.name] = str(file.absolute())
|
134 |
+
self.model_list['None'] = None
|
135 |
+
|
136 |
+
def title(self):
|
137 |
+
return "StableSR"
|
138 |
+
|
139 |
+
def show(self, is_img2img):
|
140 |
+
return is_img2img
|
141 |
+
|
142 |
+
def ui(self, is_img2img):
|
143 |
+
with gr.Row():
|
144 |
+
model = gr.Dropdown(list(self.model_list.keys()), label="SR Model")
|
145 |
+
refresh = gr.Button(value='↻', variant='tool')
|
146 |
+
def refresh_fn(selected):
|
147 |
+
self.load_model_list()
|
148 |
+
if selected not in self.model_list:
|
149 |
+
selected = 'None'
|
150 |
+
return gr.Dropdown.update(value=selected, choices=list(self.model_list.keys()))
|
151 |
+
refresh.click(fn=refresh_fn,inputs=model, outputs=model)
|
152 |
+
with gr.Row():
|
153 |
+
scale_factor = gr.Slider(minimum=1, maximum=16, step=0.1, value=2, label='Scale Factor', elem_id=f'StableSR-scale')
|
154 |
+
with gr.Row():
|
155 |
+
color_fix = gr.Dropdown(['None', 'Wavelet', 'AdaIN'], label="Color Fix", value='Wavelet', elem_id=f'StableSR-color-fix')
|
156 |
+
save_original = gr.Checkbox(label='Save Original', value=False, elem_id=f'StableSR-save-original', visible=color_fix.value != 'None')
|
157 |
+
color_fix.change(fn=lambda selected: gr.Checkbox.update(visible=selected != 'None'), inputs=color_fix, outputs=save_original, show_progress=False)
|
158 |
+
pure_noise = gr.Checkbox(label='Pure Noise', value=True, elem_id=f'StableSR-pure-noise')
|
159 |
+
unload_model= gr.Button(value='Unload Model', variant='tool')
|
160 |
+
def unload_model_fn():
|
161 |
+
if self.stablesr_model is not None:
|
162 |
+
self.stablesr_model = None
|
163 |
+
devices.torch_gc()
|
164 |
+
print('[StableSR] Model unloaded!')
|
165 |
+
else:
|
166 |
+
print('[StableSR] No model loaded.')
|
167 |
+
unload_model.click(fn=unload_model_fn)
|
168 |
+
return [model, scale_factor, pure_noise, color_fix, save_original]
|
169 |
+
|
170 |
+
def run(self, p: StableDiffusionProcessingImg2Img, model: str, scale_factor:float, pure_noise: bool, color_fix:str, save_original:bool) -> Processed:
|
171 |
+
|
172 |
+
if model == 'None':
|
173 |
+
# do clean up
|
174 |
+
self.stablesr_model = None
|
175 |
+
self.last_model_path = None
|
176 |
+
return
|
177 |
+
|
178 |
+
if model not in self.model_list:
|
179 |
+
raise gr.Error(f"Model {model} is not in the list! Please refresh your browser!")
|
180 |
+
|
181 |
+
if not os.path.exists(self.model_list[model]):
|
182 |
+
raise gr.Error(f"Model {model} is not on your disk! Please refresh the model list!")
|
183 |
+
|
184 |
+
if color_fix not in ['None', 'Wavelet', 'AdaIN']:
|
185 |
+
print(f'[StableSR] Invalid color fix method: {color_fix}')
|
186 |
+
color_fix = 'None'
|
187 |
+
|
188 |
+
# upscale the image, set the ouput size
|
189 |
+
init_img: Image = p.init_images[0]
|
190 |
+
target_width = int(init_img.width * scale_factor)
|
191 |
+
target_height = int(init_img.height * scale_factor)
|
192 |
+
# if the target width is not dividable by 8, then round it up
|
193 |
+
if target_width % 8 != 0:
|
194 |
+
target_width = target_width + 8 - target_width % 8
|
195 |
+
# if the target height is not dividable by 8, then round it up
|
196 |
+
if target_height % 8 != 0:
|
197 |
+
target_height = target_height + 8 - target_height % 8
|
198 |
+
init_img = init_img.resize((target_width, target_height), Image.LANCZOS)
|
199 |
+
p.init_images[0] = init_img
|
200 |
+
p.width = init_img.width
|
201 |
+
p.height = init_img.height
|
202 |
+
|
203 |
+
print('[StableSR] Target image size: {}x{}'.format(init_img.width, init_img.height))
|
204 |
+
|
205 |
+
first_param = shared.sd_model.parameters().__next__()
|
206 |
+
if self.last_path != self.model_list[model]:
|
207 |
+
# load the model
|
208 |
+
self.stablesr_model = None
|
209 |
+
|
210 |
+
if self.stablesr_model is None:
|
211 |
+
self.stablesr_model = StableSR(self.model_list[model], dtype=first_param.dtype, device=first_param.device)
|
212 |
+
self.last_path = self.model_list[model]
|
213 |
+
|
214 |
+
def sample_custom(conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts):
|
215 |
+
try:
|
216 |
+
unet: UNetModel = shared.sd_model.model.diffusion_model
|
217 |
+
self.stablesr_model.hook(unet)
|
218 |
+
self.stablesr_model.set_latent_image(p.init_latent)
|
219 |
+
x = processing.create_random_tensors(p.init_latent.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, seed_resize_from_h=p.seed_resize_from_h, seed_resize_from_w=p.seed_resize_from_w, p=p)
|
220 |
+
sampler = sd_samplers.create_sampler(p.sampler_name, p.sd_model)
|
221 |
+
if pure_noise:
|
222 |
+
# NOTE: use txt2img instead of img2img sampling
|
223 |
+
samples = sampler.sample(p, x, conditioning, unconditional_conditioning, image_conditioning=p.image_conditioning)
|
224 |
+
else:
|
225 |
+
if p.initial_noise_multiplier != 1.0:
|
226 |
+
p.extra_generation_params["Noise multiplier"] =p.initial_noise_multiplier
|
227 |
+
x *= p.initial_noise_multiplier
|
228 |
+
samples = sampler.sample_img2img(p, p.init_latent, x, conditioning, unconditional_conditioning, image_conditioning=p.image_conditioning)
|
229 |
+
|
230 |
+
if p.mask is not None:
|
231 |
+
samples = samples * p.nmask + p.init_latent * p.mask
|
232 |
+
del x
|
233 |
+
devices.torch_gc()
|
234 |
+
return samples
|
235 |
+
finally:
|
236 |
+
self.stablesr_model.unhook(unet)
|
237 |
+
# in --medvram and --lowvram mode, we send the model back to the initial device
|
238 |
+
self.stablesr_model.struct_cond_model.to(device=first_param.device)
|
239 |
+
self.stablesr_model.spade_layers.to(device=first_param.device)
|
240 |
+
|
241 |
+
|
242 |
+
# replace the sample function
|
243 |
+
p.sample = sample_custom
|
244 |
+
|
245 |
+
if color_fix != 'None':
|
246 |
+
p.do_not_save_samples = True
|
247 |
+
|
248 |
+
result: Processed = processing.process_images(p)
|
249 |
+
|
250 |
+
if color_fix != 'None':
|
251 |
+
|
252 |
+
fixed_images = []
|
253 |
+
# fix the color
|
254 |
+
color_fix_func = wavelet_color_fix if color_fix == 'Wavelet' else adain_color_fix
|
255 |
+
for i in range(len(result.images)):
|
256 |
+
try:
|
257 |
+
fixed_images.append(color_fix_func(result.images[i], init_img))
|
258 |
+
except Exception as e:
|
259 |
+
print(f'[StableSR] Error fixing color with default method: {e}')
|
260 |
+
|
261 |
+
# save the fixed color images
|
262 |
+
for i in range(len(fixed_images)):
|
263 |
+
try:
|
264 |
+
images.save_image(fixed_images[i], p.outpath_samples, "", p.all_seeds[i], p.all_prompts[i], opts.samples_format, info=result.infotexts[i], p=p)
|
265 |
+
except Exception as e:
|
266 |
+
print(f'[StableSR] Error saving color fixed image: {e}')
|
267 |
+
|
268 |
+
if save_original:
|
269 |
+
for i in range(len(result.images)):
|
270 |
+
try:
|
271 |
+
images.save_image(result.images[i], p.outpath_samples, "", p.all_seeds[i], p.all_prompts[i], opts.samples_format, info=result.infotexts[i], p=p, suffix="-before-color-fix")
|
272 |
+
except Exception as e:
|
273 |
+
print(f'[StableSR] Error saving original image: {e}')
|
274 |
+
result.images = result.images + fixed_images
|
275 |
+
|
276 |
+
return result
|
sd-webui-stablesr/srmodule/__pycache__/attn.cpython-310.pyc
ADDED
Binary file (3.29 kB). View file
|
|
sd-webui-stablesr/srmodule/__pycache__/colorfix.cpython-310.pyc
ADDED
Binary file (3.41 kB). View file
|
|