Refactor: combine insert_keyframe() and insert_key_rna() into a single function #122053

Merged
Nathan Vegdahl merged 49 commits from nathanvegdahl/blender:combine_keying_functions into main 2024-06-11 16:43:08 +02:00
210 changed files with 5234 additions and 2333 deletions
Showing only changes of commit 25d50e848d - Show all commits

View File

@ -177,27 +177,27 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
const float cos_a = dot(B, C);
const float cos_b = dot(A, C);
const float cos_c = dot(A, B);
const float sin_b_sin_c_2 = (1.0f - sqr(cos_b)) * (1.0f - sqr(cos_c));
const float mixed_product = fabsf(dot(A, cross(B, C)));
/* The area of the spherical triangle is equal to the subtended solid angle. */
const float solid_angle = 2.0f * fast_atan2f(mixed_product, (1.0f + cos_a + cos_b + cos_c));
/* Compute the angle at A. */
const float cos_alpha = dot(safe_normalize(cross(A, B)), safe_normalize(cross(A, C)));
const float sin_alpha = sin_from_cos(cos_alpha);
const float alpha = safe_acosf(cos_alpha);
/* Select a random sub-area of the spherical triangle and calculate the third vertex C_ of that
* new triangle. */
const float A_hat = rand.x * solid_angle;
float sin_A_hat, cos_A_hat;
fast_sincosf(A_hat, &sin_A_hat, &cos_A_hat);
/* These values lack a `sin_b * sin_c` factor, will divide when computing `temp`. */
const float cos_alpha = cos_a - cos_b * cos_c;
const float sin_alpha = mixed_product;
const float t = cos_A_hat * cos_alpha + sin_A_hat * sin_alpha;
const float temp = (cos_c - 1.0f) * t * cos_alpha / sin_b_sin_c_2;
const float q = (cos_A_hat - cos_c + temp) / (1.0f - cos_A_hat * cos_c + temp);
float sin_phi, cos_phi;
fast_sincosf(A_hat - alpha, &sin_phi, &cos_phi);
const float u = cos_phi - cos_alpha;
const float v = sin_phi + sin_alpha * cos_c;
const float num = (v * cos_phi - u * sin_phi) * cos_alpha - v;
const float den = (v * sin_phi + u * cos_phi) * sin_alpha;
const float q = (den == 0.0f) ? 1.0f : num / den;
const float3 U = safe_normalize(C - cos_b * A);
const float3 C_ = safe_normalize(q * A + sin_from_cos(q) * U);

View File

@ -2223,6 +2223,10 @@ OSL_OP_IMPL_XX(osl_abs, fabsf)
OSL_OP_IMPL_II(osl_fabs, abs)
OSL_OP_IMPL_XX(osl_fabs, fabsf)
OSL_OP_IMPL_XXX(osl_fmod, safe_modulo)
OSL_OP_IMPL_VVF_(osl_fmod, safe_modulo)
OSL_OP_IMPL_DVVDF_(osl_fmod, safe_modulo)
OSL_OP_IMPL_DVDVF_(osl_fmod, safe_modulo)
OSL_OP_IMPL_DVDVDF_(osl_fmod, safe_modulo)
OSL_OP_IMPL_FFFF(osl_smoothstep, smoothstep)
OSL_OP_IMPL_DFFFDF(osl_smoothstep, smoothstep)

View File

@ -45,9 +45,9 @@
inkscape:window-height="1009"
id="namedview34335"
showgrid="true"
inkscape:zoom="5.6568544"
inkscape:cx="347.80814"
inkscape:cy="81.052113"
inkscape:zoom="4.0000001"
inkscape:cx="28.374999"
inkscape:cy="452.12499"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
@ -10774,94 +10774,6 @@
d="m 305.5,100 a 0.50005,0.50005 0 0 0 -0.5,0.5 v 3 a 0.50005,0.50005 0 0 0 0.5,0.5 h 3 a 0.50005,0.50005 0 0 0 0.5,-0.5 v -3 a 0.50005,0.50005 0 0 0 -0.5,-0.5 z m 0.5,1 h 2 v 2 h -2 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
id="g38065"
inkscape:label="V-7"
style="display:inline;enable-background:new">
<path
id="path6600"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 138.51562,452 c 0.752,0 1.45382,0.239 2.02344,0.64453 0.88567,0.63055 1.46094,1.67061 1.46094,2.83985 V 458.5 c -3e-5,0.27537 -0.22268,0.4989 -0.49805,0.5 l -4.00976,0.008 c -0.27613,-3e-5 -0.49997,-0.22387 -0.5,-0.5 L 137,452.53516 v -0.002 -0.0352 c 0.001,-0.27524 0.22466,-0.49793 0.5,-0.49796 z M 136,452 v 1 h -1.51563 C 133.09986,453 132,454.09985 132,455.48438 v 8.03124 c 0,1.38452 1.09986,2.48438 2.48437,2.48438 h 4.03125 C 139.90014,466 141,464.90014 141,463.51562 V 460 h 1 v 3.51562 C 142,465.43685 140.43685,467 138.51562,467 h -4.03125 C 132.56315,467 131,465.43686 131,463.51562 v -8.03124 C 131,453.56315 132.56315,452 134.48437,452 Z m 7.47656,-0.97852 a 0.50005,0.50005 0 0 0 -0.31445,0.8711 c 0.58809,0.54453 0.8418,1.0856 0.8418,1.60156 L 144,459.5 a 0.50005,0.50005 0 1 0 1,0 l 0.004,-6.00586 c 0,-0.8509 -0.43179,-1.65971 -1.16211,-2.33594 a 0.50005,0.50005 0 0 0 -0.36524,-0.13672 z m 3.01563,2.01563 A 0.50005,0.50005 0 0 0 146,453.54297 v 4.05859 a 0.50005,0.50005 0 1 0 1,0 v -4.05859 a 0.50005,0.50005 0 0 0 -0.50781,-0.50586 z"
inkscape:connector-curvature="0" />
</g>
<g
id="g38062"
inkscape:label="V-6"
style="display:inline;enable-background:new">
<path
id="path6598"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 113.48438,452 C 111.56315,452 110,453.56315 110,455.48438 v 8.03124 c 0,1.92123 1.56315,3.48438 3.48438,3.48438 h 4.03125 C 119.43685,467 121,465.43685 121,463.51562 v -8.03124 C 121,453.56315 119.43685,452 117.51563,452 Z m 0,1 h 4.03125 c 1.38452,0 2.48437,1.09985 2.48437,2.48438 v 8.03124 C 120,464.90015 118.90015,466 117.51563,466 h -4.03125 C 112.09985,466 111,464.90015 111,463.51562 v -8.03124 C 111,454.09985 112.09985,453 113.48438,453 Z m 1.5625,1 C 114.47554,454 114,454.47555 114,455.04688 v 3.90624 c 0,0.57133 0.47554,1.04688 1.04688,1.04688 h 0.90625 C 116.52446,460 117,459.52445 117,458.95312 v -3.90624 C 117,454.47555 116.52446,454 115.95313,454 Z m 7.42968,-2.97852 a 0.50005,0.50005 0 0 0 -0.31445,0.8711 c 0.58809,0.54453 0.8418,1.0856 0.8418,1.60156 L 123,459.5 a 0.50005,0.50005 0 1 0 1,0 l 0.004,-6.00586 c 0,-0.8509 -0.43179,-1.65971 -1.16211,-2.33594 a 0.50005,0.50005 0 0 0 -0.36524,-0.13672 z m 3.01563,2.01563 A 0.50005,0.50005 0 0 0 125,453.54297 v 4.05859 a 0.50005,0.50005 0 1 0 1,0 v -4.05859 a 0.50005,0.50005 0 0 0 -0.50781,-0.50586 z"
inkscape:connector-curvature="0" />
</g>
<g
id="g38059"
inkscape:label="V-5"
style="display:inline;enable-background:new">
<path
id="path6596"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 92.484375,452 c -0.752,0 -1.453818,0.239 -2.023438,0.64453 C 89.575267,453.27508 89,454.31514 89,455.48438 V 458.5 c 3e-5,0.27537 0.222677,0.4989 0.498047,0.5 l 4.009765,0.008 c 0.27613,-3e-5 0.49997,-0.22387 0.5,-0.5 L 94,452.53516 v -0.002 -0.0352 C 93.999,452.22272 93.77534,452.00003 93.5,452 Z M 95,452 v 1 h 1.515625 C 97.900145,453 99,454.09985 99,455.48438 v 8.03124 C 99,464.90014 97.900145,466 96.515625,466 h -4.03125 C 91.099855,466 90,464.90014 90,463.51562 V 460 h -1 v 3.51562 C 89,465.43685 90.563145,467 92.484375,467 h 4.03125 C 98.436855,467 100,465.43686 100,463.51562 v -8.03124 C 100,453.56315 98.436855,452 96.515625,452 Z m 6.47656,-0.97852 a 0.50005,0.50005 0 0 0 -0.31445,0.8711 c 0.58809,0.54453 0.8418,1.0856 0.8418,1.60156 L 102,459.5 a 0.50005,0.50005 0 1 0 1,0 l 0.004,-6.00586 c 0,-0.8509 -0.43179,-1.65971 -1.16211,-2.33594 a 0.50005,0.50005 0 0 0 -0.36524,-0.13672 z m 3.01563,2.01563 A 0.50005,0.50005 0 0 0 104,453.54297 v 4.05859 a 0.50005,0.50005 0 1 0 1,0 v -4.05859 a 0.50005,0.50005 0 0 0 -0.50781,-0.50586 z"
inkscape:connector-curvature="0" />
</g>
<g
transform="translate(-84,1.45e-5)"
id="g10880"
style="display:inline;fill:#ffffff;enable-background:new"
inkscape:export-filename="blender_icons.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:label="V-4">
<g
transform="translate(-523,-56)"
id="g10853"
style="fill:#ffffff" />
<g
id="g10878"
style="fill:#ffffff">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new"
d="M 157.49219,453.99222 A 0.50005,0.50005 0 0 0 157,454.50003 v 4 a 0.50005,0.50005 0 1 0 1,0 v -4 a 0.50005,0.50005 0 0 0 -0.50781,-0.50781 z"
id="path10874"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 80.476562,451.02148 a 0.50005,0.50005 0 0 0 -0.314453,0.8711 c 0.58809,0.54453 0.841797,1.0856 0.841797,1.60156 L 81,459.5 a 0.50005,0.50005 0 1 0 1,0 l 0.0039,-6.00586 c 0,-0.8509 -0.431789,-1.65971 -1.162109,-2.33594 a 0.50005,0.50005 0 0 0 -0.365235,-0.13672 z M 71.5625,452 C 69.59752,452 68,453.59753 68,455.5625 v 7.875 C 68,465.40247 69.59752,467 71.5625,467 h 3.875 C 77.40247,467 79,465.40247 79,463.4375 v -7.875 C 79,453.59753 77.40247,452 75.4375,452 Z m 0,1 h 3.875 C 76.86577,453 78,454.13423 78,455.5625 v 7.875 C 78,464.86577 76.86577,466 75.4375,466 h -3.875 C 70.13422,466 69,464.86577 69,463.4375 v -7.875 C 69,454.13423 70.13422,453 71.5625,453 Z m 11.929688,0.0371 A 0.50005,0.50005 0 0 0 83,453.54297 v 4.05859 a 0.50005,0.50005 0 1 0 1,0 v -4.05859 a 0.50005,0.50005 0 0 0 -0.507812,-0.50586 z"
transform="translate(84,-1.45e-5)"
id="rect10876"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g38056"
inkscape:label="V-3"
style="display:inline;enable-background:new">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 56.515625,452 c 0.752,0 1.453818,0.239 2.023438,0.64453 C 59.424733,453.27508 60,454.31514 60,455.48438 V 458.5 c -3e-5,0.27537 -0.222677,0.4989 -0.498047,0.5 l -4.009765,0.008 c -0.27613,-3e-5 -0.49997,-0.22387 -0.5,-0.5 L 55,452.53516 v -0.002 -0.0352 C 55.001,452.22272 55.22466,452.00003 55.5,452 Z M 54,452 v 1 H 52.484375 C 51.099855,453 50,454.09985 50,455.48438 v 8.03124 C 50,464.90014 51.099855,466 52.484375,466 h 4.03125 C 57.900145,466 59,464.90014 59,463.51562 V 460 h 1 v 3.51562 C 60,465.43685 58.436855,467 56.515625,467 h -4.03125 C 50.563145,467 49,465.43686 49,463.51562 v -8.03124 C 49,453.56315 50.563145,452 52.484375,452 Z"
id="path6557"
inkscape:connector-curvature="0" />
</g>
<g
id="g38032"
inkscape:label="V-2"
style="display:inline;enable-background:new">
<path
inkscape:connector-curvature="0"
id="rect10947"
d="M 31.484375,452 C 29.563145,452 28,453.56315 28,455.48438 v 8.03124 C 28,465.43685 29.563145,467 31.484375,467 h 4.03125 C 37.436855,467 39,465.43685 39,463.51562 v -8.03124 C 39,453.56315 37.436855,452 35.515625,452 Z m 0,1 h 4.03125 C 36.900155,453 38,454.09985 38,455.48438 v 8.03124 C 38,464.90015 36.900155,466 35.515625,466 h -4.03125 C 30.099845,466 29,464.90015 29,463.51562 v -8.03124 C 29,454.09985 30.099845,453 31.484375,453 Z m 1.5625,1 C 32.475545,454 32,454.47555 32,455.04688 v 3.90624 C 32,459.52445 32.475545,460 33.046875,460 h 0.90625 C 34.524455,460 35,459.52445 35,458.95312 v -3.90624 C 35,454.47555 34.524455,454 33.953125,454 Z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
id="g38029"
inkscape:label="V-1"
style="display:inline;enable-background:new">
<path
inkscape:connector-curvature="0"
id="path10927"
d="m 10.484375,452 c -0.752,0 -1.4538175,0.239 -2.0234375,0.64453 C 7.5752675,453.27508 7,454.31514 7,455.48438 V 458.5 c 3e-5,0.27537 0.2226769,0.4989 0.4980469,0.5 l 4.0097651,0.008 c 0.27613,-3e-5 0.49997,-0.22387 0.5,-0.5 L 12,452.53516 v -0.002 -0.0352 C 11.999,452.22272 11.77534,452.00003 11.5,452 Z M 13,452 v 1 h 1.515625 C 15.900145,453 17,454.09985 17,455.48438 v 8.03124 C 17,464.90014 15.900145,466 14.515625,466 h -4.03125 C 9.099855,466 8,464.90014 8,463.51562 V 460 H 7 v 3.51562 C 7,465.43685 8.563145,467 10.484375,467 h 4.03125 C 16.436855,467 18,465.43686 18,463.51562 v -8.03124 C 18,453.56315 16.436855,452 14.515625,452 Z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
id="g76955"
style="display:inline;enable-background:new"
@ -19465,6 +19377,102 @@
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccc" />
</g>
<g
id="g38065"
inkscape:label="V-7"
style="display:inline;enable-background:new"
transform="translate(0.00728219,0.00606244)">
<path
id="path6600"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 138.51562,451.00563 c 0.752,0 1.45382,0.239 2.02344,0.64453 0.88567,0.63055 1.46094,1.67061 1.46094,2.83985 V 458.5 c -3e-5,0.27537 -0.22268,0.4989 -0.49805,0.5 l -4.00976,0.008 c -0.27613,-3e-5 -0.49997,-0.22387 -0.5,-0.5 L 137,451.54079 v -0.002 -0.0352 c 0.001,-0.27524 0.22466,-0.49793 0.5,-0.49796 z m -2.51562,0 v 1 h -1.51563 c -1.38451,0 -2.48437,1.09985 -2.48437,2.48438 v 9.02561 c 0,1.38452 1.09986,2.48438 2.48437,2.48438 h 4.03125 C 139.90014,466 141,464.90014 141,463.51562 V 460 h 1 v 3.51562 C 142,465.43685 140.43685,467 138.51562,467 h -4.03125 C 132.56315,467 131,465.43686 131,463.51562 v -9.02561 c 0,-1.92123 1.56315,-3.48438 3.48437,-3.48438 z m 7.47656,0.0159 c -0.44785,0.0182 -0.64741,0.57103 -0.31445,0.8711 0.58809,0.54453 0.8418,1.0856 0.8418,1.60156 L 144,459.5 c -0.01,0.67616 1.00956,0.67616 1,0 l 0.004,-6.00586 c 0,-0.8509 -0.43179,-1.65971 -1.16211,-2.33594 -0.0982,-0.0931 -0.23001,-0.14246 -0.36524,-0.13672 z m 3.01563,2.01563 c -0.27537,0.004 -0.49542,0.23047 -0.49219,0.50586 v 4.05859 c -0.01,0.67616 1.00956,0.67616 1,0 v -4.05859 c 0.003,-0.2815 -0.22632,-0.51025 -0.50781,-0.50586 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssccccccccsccssssssccsssssscsccccccsssccccs" />
</g>
<g
id="g38062"
inkscape:label="V-6"
style="display:inline;enable-background:new"
transform="translate(0.00728219,0.00606244)">
<path
id="path6598"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 113.48438,451.00563 c -1.92123,0 -3.48438,1.56315 -3.48438,3.48438 v 9.02561 c 0,1.92123 1.56315,3.48438 3.48438,3.48438 h 4.03125 C 119.43685,467 121,465.43685 121,463.51562 v -9.02561 c 0,-1.92123 -1.56315,-3.48438 -3.48437,-3.48438 z m 0,1 h 4.03125 c 1.38452,0 2.48437,1.09985 2.48437,2.48438 v 9.02561 C 120,464.90015 118.90015,466 117.51563,466 h -4.03125 C 112.09985,466 111,464.90015 111,463.51562 v -9.02561 c 0,-1.38453 1.09985,-2.48438 2.48438,-2.48438 z m 1.5625,1 c -0.57134,0 -1.04688,0.47555 -1.04688,1.04688 v 4.90061 c 0,0.57133 0.47554,1.04688 1.04688,1.04688 h 0.90625 C 116.52446,460 117,459.52445 117,458.95312 v -4.90061 c 0,-0.57133 -0.47554,-1.04688 -1.04687,-1.04688 z m 7.42968,-1.98415 c -0.44785,0.0182 -0.64741,0.57103 -0.31445,0.8711 0.58809,0.54453 0.8418,1.0856 0.8418,1.60156 L 123,459.5 c -0.01,0.67616 1.00956,0.67616 1,0 l 0.004,-6.00586 c 0,-0.8509 -0.43179,-1.65971 -1.16211,-2.33594 -0.0982,-0.0931 -0.23001,-0.14246 -0.36524,-0.13672 z m 3.01563,2.01563 c -0.27537,0.004 -0.49542,0.23047 -0.49219,0.50586 v 4.05859 c -0.01,0.67616 1.00956,0.67616 1,0 v -4.05859 c 0.003,-0.2815 -0.22632,-0.51025 -0.50781,-0.50586 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssssssssssssssssssssssssssssccccccsssccccs" />
</g>
<g
id="g38059"
inkscape:label="V-5"
style="display:inline;enable-background:new"
transform="translate(0.00728219,0.00606244)">
<path
id="path6596"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 92.484375,451.01563 c -0.752,0 -1.453818,0.239 -2.023438,0.64453 C 89.575267,452.29071 89,453.33077 89,454.50001 V 458.5 c 3e-5,0.27537 0.222677,0.4989 0.498047,0.5 l 4.009765,0.008 c 0.27613,-3e-5 0.49997,-0.22387 0.5,-0.5 L 94,451.55079 v -0.002 -0.0352 c -0.001,-0.27524 -0.22466,-0.49793 -0.5,-0.49796 z m 2.515625,0 v 1 h 1.515625 c 1.38452,0 2.484375,1.09985 2.484375,2.48438 v 9.01561 C 99,464.90014 97.900145,466 96.515625,466 h -4.03125 C 91.099855,466 90,464.90014 90,463.51562 V 460 h -1 v 3.51562 C 89,465.43685 90.563145,467 92.484375,467 h 4.03125 C 98.436855,467 100,465.43686 100,463.51562 v -9.01561 c 0,-1.92123 -1.563145,-3.48438 -3.484375,-3.48438 z m 6.47656,0.006 c -0.44785,0.0182 -0.64741,0.57103 -0.31445,0.8711 0.58809,0.54453 0.8418,1.0856 0.8418,1.60156 L 102,459.5 c -0.01,0.67616 1.00956,0.67616 1,0 l 0.004,-6.00586 c 0,-0.8509 -0.43179,-1.65971 -1.16211,-2.33594 -0.0982,-0.0931 -0.23001,-0.14246 -0.36524,-0.13672 z m 3.01563,2.01563 c -0.27537,0.004 -0.49542,0.23047 -0.49219,0.50586 v 4.05859 c -0.01,0.67616 1.00956,0.67616 1,0 v -4.05859 c 0.003,-0.2815 -0.22632,-0.51025 -0.50781,-0.50586 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssccccccccsccssssssccsssssscsccccccsssccccs" />
</g>
<g
transform="translate(-83.992713,0.00607694)"
id="g10880"
style="display:inline;fill:#ffffff;enable-background:new"
inkscape:export-filename="blender_icons.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:label="V-4">
<g
transform="translate(-523,-56)"
id="g10853"
style="fill:#ffffff" />
<g
id="g10878"
style="fill:#ffffff">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 80.476562,451.02148 c -0.447856,0.0182 -0.647418,0.57103 -0.314453,0.8711 0.58809,0.54453 0.841797,1.0856 0.841797,1.60156 L 81,459.5 c -0.0096,0.67616 1.009563,0.67616 1,0 l 0.0039,-6.00586 c 0,-0.8509 -0.431789,-1.65971 -1.162109,-2.33594 -0.09822,-0.0931 -0.230004,-0.14246 -0.365235,-0.13672 z M 71.5625,451 C 69.59752,451 68,452.59753 68,454.5625 v 8.875 C 68,465.40247 69.59752,467 71.5625,467 h 3.875 C 77.40247,467 79,465.40247 79,463.4375 v -8.875 C 79,452.59753 77.40247,451 75.4375,451 Z m 0,1 h 3.875 C 76.86577,452 78,453.13423 78,454.5625 v 8.875 C 78,464.86577 76.86577,466 75.4375,466 h -3.875 C 70.13422,466 69,464.86577 69,463.4375 v -8.875 C 69,453.13423 70.13422,452 71.5625,452 Z m 11.929688,1.0371 c -0.275376,0.004 -0.495427,0.23048 -0.492188,0.50587 v 4.05859 c -0.0096,0.67616 1.009563,0.67616 1,0 v -4.05859 c 0.0033,-0.2815 -0.226323,-0.51025 -0.507812,-0.50586 z"
transform="translate(84,-1.45e-5)"
id="rect10876"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sccccccsssssssssssssssssssssccccss" />
</g>
</g>
<g
id="g38056"
inkscape:label="V-3"
style="display:inline;enable-background:new"
transform="translate(0.00728219,0.00606244)">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 57.512484,450.98438 c 0.752,0 1.453818,0.239 2.023438,0.64453 0.88567,0.63055 1.460937,1.67061 1.460937,2.83985 V 458.5 c -3e-5,0.27537 -0.222677,0.4989 -0.498047,0.5 l -4.009765,0.008 c -0.27613,-3e-5 -0.49997,-0.22387 -0.5,-0.5 l 0.0078,-6.98846 v -0.002 -0.0352 c 10e-4,-0.27524 0.22466,-0.49793 0.5,-0.49796 z m -2.515625,0 v 1 H 52.484375 C 51.099855,451.98438 50,453.08423 50,454.46876 v 9.04686 C 50,464.90014 51.099855,466 52.484375,466 h 5.028109 c 1.38452,0 2.484375,-1.09986 2.484375,-2.48438 V 460 h 1 v 3.51562 c 0,1.92123 -1.563145,3.48438 -3.484375,3.48438 H 52.484375 C 50.563145,467 49,465.43686 49,463.51562 v -9.04686 c 0,-1.92123 1.563145,-3.48438 3.484375,-3.48438 z"
id="path6557"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssccccccccsccssssssccssssssc" />
</g>
<g
id="g38032"
inkscape:label="V-2"
style="display:inline;enable-background:new"
transform="matrix(0.99598235,0,0,1,0.11977619,0.00606244)">
<path
inkscape:connector-curvature="0"
id="rect10947"
d="M 31.484375,450.99458 C 29.563145,450.99458 28,452.55773 28,454.47896 v 9.03666 C 28,465.43685 29.563145,467 31.484375,467 h 5.082348 c 1.92123,0 3.484375,-1.56315 3.484375,-3.48438 v -9.03666 c 0,-1.92123 -1.563145,-3.48438 -3.484375,-3.48438 z m 0,1 h 5.082348 c 1.38453,0 2.484375,1.09985 2.484375,2.48438 v 9.03666 c 0,1.38453 -1.099845,2.48438 -2.484375,2.48438 H 31.484375 C 30.099845,466 29,464.90015 29,463.51562 v -9.03666 c 0,-1.38453 1.099845,-2.48438 2.484375,-2.48438 z m 1.5625,1 C 32.475545,452.99458 32,453.47013 32,454.04146 v 4.91166 C 32,459.52445 32.475545,460 33.046875,460 h 1.973036 c 0.57133,0 1.046875,-0.47555 1.046875,-1.04688 v -4.91166 c 0,-0.57133 -0.475545,-1.04688 -1.046875,-1.04688 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
sodipodi:nodetypes="sssssssssssssssssssssssssss" />
</g>
<g
id="g38029"
inkscape:label="V-1"
style="display:inline;enable-background:new"
transform="translate(0.00728219,0.00606244)">
<path
inkscape:connector-curvature="0"
id="path10927"
d="m 10.484375,450.98353 c -0.752,0 -1.4538175,0.239 -2.0234375,0.64453 C 7.5752675,452.25861 7,453.29867 7,454.46791 V 458.5 c 3e-5,0.27537 0.2226769,0.4989 0.4980469,0.5 l 4.0097651,0.008 c 0.27613,-3e-5 0.49997,-0.22387 0.5,-0.5 L 12,451.51869 v -0.002 -0.0352 c -10e-4,-0.27524 -0.22466,-0.49793 -0.5,-0.49796 z m 2.515625,0 v 1 h 2.508368 c 1.38452,0 2.484375,1.09985 2.484375,2.48438 v 9.04771 c 0,1.38452 -1.099855,2.48438 -2.484375,2.48438 H 10.484375 C 9.099855,466 8,464.90014 8,463.51562 V 460 H 7 v 3.51562 C 7,465.43685 8.563145,467 10.484375,467 h 5.023993 c 1.92123,0 3.484375,-1.56314 3.484375,-3.48438 v -9.04771 c 0,-1.92123 -1.563145,-3.48438 -3.484375,-3.48438 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
sodipodi:nodetypes="sssccccccccsccssssssccssssssc" />
</g>
</g>
<g
inkscape:groupmode="layer"

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@ -80,7 +80,7 @@ displays:
- !<View> {name: Raw, colorspace: Non-Color}
active_displays: [sRGB, Display P3, Rec.1886, Rec.2020]
active_views: [Standard, Khronos PBR Neutral, AgX, Filmic, Filmic Log, False Color, Raw]
inactive_colorspaces: [Luminance Compensation Rec.2020, Luminance Compensation sRGB, Luminance Compensation P3, AgX False Color Rec.709, AgX False Color P3, AgX False Color Rec.1886, AgX False Color Rec.2020]
inactive_colorspaces: [Luminance Compensation Rec.2020, Luminance Compensation sRGB, Luminance Compensation P3, AgX False Color Rec.709, AgX False Color P3, AgX False Color Rec.2020]
colorspaces:
- !<ColorSpace>

View File

@ -78,7 +78,7 @@ const UserDef U_default = {
.virtual_pixel = 0,
.scrollback = 256,
.node_margin = 80,
.node_margin = 40,
.node_preview_res = 120,
.transopts = USER_TR_TOOLTIPS,
.menuthreshold1 = 5,

View File

@ -88,6 +88,9 @@ watch_test_cli_blender: FORCE
tput clear; \
done
test_path_pattern_match: FORCE
@env --chdir="$(BASE_DIR)" \
$(PYTHON_BIN) ./tests/test_path_pattern_match.py
# https://www.cyberciti.biz/faq/howto-create-linux-ram-disk-filesystem/
# mkfs -q /dev/ram1 8192
@ -104,6 +107,14 @@ watch_test: FORCE
tput clear; \
done
watch_test_path_pattern_match: FORCE
@cd "$(BASE_DIR)" && \
while true; do \
$(MAKE) test_path_pattern_match; \
inotifywait -q -e close_write $(EXTRA_WATCH_FILES) $(PY_FILES) ; \
tput clear; \
done
watch_check_mypy:
@cd "$(BASE_DIR)" && \
while true; do \

View File

@ -143,6 +143,7 @@ def repos_to_notify():
# Since it's not all that common to disable the status bar just run notifications
# if any repositories are marked to run notifications.
online_access = bpy.app.online_access
prefs = bpy.context.preferences
extension_repos = prefs.extensions.repos
for repo_item in extension_repos:
@ -152,9 +153,19 @@ def repos_to_notify():
continue
if not repo_item.use_remote_url:
continue
remote_url = repo_item.remote_url
# Invalid, if there is no remote path this can't update.
if not repo_item.remote_url:
if not remote_url:
continue
if online_access:
# All URL's may be accessed.
pass
else:
# Allow remote file-system repositories even when online access is disabled.
if not remote_url.startswith("file://"):
continue
repos_notify.append(repo_item)
return repos_notify
@ -434,14 +445,12 @@ def register():
)
WindowManager.extension_type = EnumProperty(
items=(
('ALL', "All", "Show all extensions"),
None,
('ADDON', "Add-ons", "Only show add-ons"),
('THEME', "Themes", "Only show themes"),
),
name="Filter by Type",
description="Show extensions by type",
default='ALL',
default='ADDON',
)
WindowManager.extension_enabled_only = BoolProperty(
name="Show Enabled Extensions",

View File

@ -2274,10 +2274,11 @@ class BlPkgOnlineAccess(Operator):
# While not expected, we want to know if this ever occurs, don't fail silently.
self.report({'WARNING'}, "Repository \"{:s}\" not found!".format(remote_url))
# Run the first check for updates automatically.
# Invoke the modal operator so users can cancel by pressing "Escape".
assert bpy.ops.bl_pkg.repo_sync_all.poll()
bpy.ops.bl_pkg.repo_sync_all('INVOKE_DEFAULT')
if bpy.app.online_access:
# Run the first check for updates automatically.
# Invoke the modal operator so users can cancel by pressing "Escape".
assert bpy.ops.bl_pkg.repo_sync_all.poll()
bpy.ops.bl_pkg.repo_sync_all('INVOKE_DEFAULT')
prefs.extensions.use_online_access_handled = True

View File

@ -299,7 +299,7 @@ def extensions_panel_draw_online_extensions_request_impl(
"Welcome! Access community-made add-ons and themes from the",
"extensions.blender.org repository.",
"",
"This requires internet access.",
"This also requires internet access which must be enabled in \"System\" preferences.",
):
layout_panel.label(text=line)

View File

@ -223,6 +223,67 @@ class PkgRepoData(NamedTuple):
data: List[Dict[str, Any]]
class PkgManifest_Build(NamedTuple):
"""Package Build Information (for the "build" sub-command)."""
paths: Optional[List[str]]
paths_exclude_pattern: Optional[List[str]]
@staticmethod
def _from_dict_impl(
manifest_build_dict: Dict[str, Any],
*,
extra_paths: List[str],
all_errors: bool,
) -> Union["PkgManifest_Build", List[str]]:
# TODO: generalize the type checks, see: `pkg_manifest_is_valid_or_error_impl`.
error_list = []
if value := manifest_build_dict.get("paths"):
if not isinstance(value, list):
error_list.append("[build]: \"paths\" must be a list, not a {!r}".format(type(value)))
else:
value = value + extra_paths
if (error := pkg_manifest_validate_field_build_path_list(value, strict=True)) is not None:
error_list.append(error)
if not all_errors:
return error_list
paths = value
else:
paths = None
if value := manifest_build_dict.get("paths_exclude_pattern"):
if not isinstance(value, list):
error_list.append("[build]: \"paths_exclude_pattern\" must be a list, not a {!r}".format(type(value)))
elif (error := pkg_manifest_validate_field_any_list_of_non_empty_strings(value, strict=True)) is not None:
error_list.append(error)
if not all_errors:
return error_list
paths_exclude_pattern = value
else:
paths_exclude_pattern = None
if (paths is not None) and (paths_exclude_pattern is not None):
error_list.append("[build]: declaring both \"paths\" and \"paths_exclude_pattern\" is not supported")
if error_list:
return error_list
return PkgManifest_Build(
paths=paths,
paths_exclude_pattern=paths_exclude_pattern,
)
@staticmethod
def from_dict_all_errors(
manifest_build_dict: Dict[str, Any],
extra_paths: List[str],
) -> Union["PkgManifest_Build", List[str]]:
return PkgManifest_Build._from_dict_impl(
manifest_build_dict,
extra_paths=extra_paths,
all_errors=True,
)
class PkgManifest(NamedTuple):
"""Package Information."""
schema_version: str
@ -311,7 +372,7 @@ def scandir_recursive_impl(
base_path: str,
path: str,
*,
filter_fn: Callable[[str], bool],
filter_fn: Callable[[str, bool], bool],
) -> Generator[Tuple[str, str], None, None]:
"""Recursively yield DirEntry objects for given directory."""
for entry in os.scandir(path):
@ -321,10 +382,11 @@ def scandir_recursive_impl(
entry_path = entry.path
entry_path_relateive = os.path.relpath(entry_path, base_path)
if not filter_fn(entry_path_relateive):
is_dir = entry.is_dir()
if not filter_fn(entry_path_relateive, is_dir):
continue
if entry.is_dir():
if is_dir:
yield from scandir_recursive_impl(
base_path,
entry_path,
@ -336,11 +398,33 @@ def scandir_recursive_impl(
def scandir_recursive(
path: str,
filter_fn: Callable[[str], bool],
filter_fn: Callable[[str, bool], bool],
) -> Generator[Tuple[str, str], None, None]:
yield from scandir_recursive_impl(path, path, filter_fn=filter_fn)
def build_paths_expand_iter(
path: str,
path_list: List[str],
) -> Generator[Tuple[str, str], None, None]:
"""
Expand paths from a path list which always uses "/" slashes.
"""
path_swap = os.sep != "/"
path_strip = path.rstrip(os.sep)
for filepath in path_list:
if path_swap:
filepath = filepath.replace("/", "\\")
# Avoid `os.path.join(path, filepath)` because `path` is ignored `filepath` is an absolute path.
# In the contest of declaring build paths we *never* want to reference an absolute directory
# such as `C:\path` or `/tmp/path`.
yield (
"{:s}{:s}{:s}".format(path_strip, os.sep, filepath.lstrip(os.sep)),
filepath,
)
def filepath_skip_compress(filepath: str) -> bool:
"""
Return true when this file shouldn't be compressed while archiving.
@ -620,6 +704,204 @@ def zipfile_make_root_directory(
filelist.append(member)
# -----------------------------------------------------------------------------
# Path Matching
class PathPatternMatch:
"""
A pattern matching class that takes a list of patterns and has a ``test_path`` method.
Patterns:
- Matching that follows ``gitignore`` logic.
Paths (passed to the ``test_path`` method):
- All paths must use forward slashes.
- All paths must be normalized and have no leading ``.`` or ``/`` characters.
- Directories end with a trailing ``/``.
Other notes:
- Pattern matching doesn't require the paths to exist on the file-system.
"""
# Implementation Notes:
# - Path patterns use UNIX style glob.
# - Matching doesn't depend on the order patterns are declared.
# - Use of REGEX is an implementation detail, not exposed to the API.
# - Glob uses using `fnmatch.translate` however this does not allow `*`
# to delimit on `/` which is necessary for `gitignore` style matching.
# So `/` are replaced with newlines, then REGEX multi-line logic is used
# to delimit the separators.
# - This is used for building packages, so it doesn't have to to especially fast,
# although it shouldn't cause noticeable delays at build time.
# - The test is located in: `../cli/test_path_pattern_match.py`
__slots__ = (
"_regex_list",
)
def __init__(self, path_patterns: List[str]):
self._regex_list: List[Tuple[bool, re.Pattern[str]]] = PathPatternMatch._pattern_match_as_regex(path_patterns)
def test_path(self, path: str) -> bool:
assert not path.startswith("/")
path_test = path.rstrip("/").replace("/", "\n")
if path.endswith("/"):
path_test = path_test + "/"
# For debugging.
# print("`" + path_test + "`")
result = False
for negate, regex in self._regex_list:
if regex.match(path_test):
if negate:
result = False
break
# Match but don't break as this may be negated in the future.
result = True
return result
# Internal implementation.
@staticmethod
def _pattern_match_as_regex_single(pattern: str) -> str:
from fnmatch import translate
# Special case: `!` literal prefix, needed to avoid this being handled as negation.
if pattern.startswith("\\!"):
pattern = pattern[1:]
# Avoid confusing pattern matching logic, strip duplicate slashes.
while True:
pattern_next = pattern.replace("//", "/")
if len(pattern_next) != len(pattern):
pattern = pattern_next
else:
del pattern_next
break
# Remove redundant leading/trailing "**"
# Besides being redundant, they break `pattern_double_star_indices` checks below.
while True: # Strip end.
if pattern.endswith("/**/"):
pattern = pattern[:-3]
elif pattern.endswith("/**"):
pattern = pattern[:-2]
else:
break
while True: # Strip start.
if pattern.startswith("/**/"):
pattern = pattern[4:]
elif pattern.startswith("**/"):
pattern = pattern[3:]
else:
break
while True: # Strip middle.
pattern_next = pattern.replace("/**/**/", "/**/")
if len(pattern_next) != len(pattern):
pattern = pattern_next
else:
del pattern_next
break
any_prefix = True
only_directory = False
if pattern.startswith("/"):
any_prefix = False
pattern = pattern.lstrip("/")
if pattern.endswith("/"):
only_directory = True
pattern = pattern.rstrip("/")
# Separate components:
pattern_split = pattern.split("/")
pattern_double_star_indices = []
for i, elem in enumerate(pattern_split):
if elem == "**":
# Note on `**` matching.
# Supporting this is complicated considerably by having to support
# `a/**/b` matching `a/b` (as well as `a/x/b` & `a/x/y/z/b`).
# Without the requirement to match `a/b` we could simply do this:
# `pattern_split[i] = "(?s:.*)"`
# However that assumes a path separator before & after the expression.
#
# Instead, build a list of double-star indices which are joined to the surrounding elements.
pattern_double_star_indices.append(i)
continue
# Some manipulation is needed for the REGEX result of `translate`.
#
# - Always adds an "end-of-string" match which isn't desired here.
#
elem_regex = translate(pattern_split[i]).removesuffix("\\Z")
# Don't match newlines.
if elem_regex.startswith("(?s:"):
elem_regex = "(?:" + elem_regex[4:]
pattern_split[i] = elem_regex
for i in reversed(pattern_double_star_indices):
assert pattern_split[i] == "**"
pattern_triple = pattern_split[i - 1: i + 2]
assert len(pattern_triple) == 3
assert pattern_triple[1] == "**"
del pattern_split[i - 1:i + 1]
pattern_split[i - 1] = (
pattern_triple[0] +
"(?:\n|\n(?s:.*)\n)?" +
pattern_triple[2]
)
del pattern_double_star_indices
# Convert path separators.
pattern = "\\n".join(pattern_split)
if any_prefix:
# Ensure the preceding text ends with a slash (or nothing).
pattern = "(?s:.*)^" + pattern
else:
# Match string start (not line start).
pattern = "\\A" + pattern
if only_directory:
# Ensure this only ever matches a directly.
pattern = pattern + "[\\n/]"
else:
# Ensure this isn't part of a longer string.
pattern = pattern + "/?\\Z"
return pattern
@staticmethod
def _pattern_match_as_regex(path_patterns: Sequence[str]) -> List[Tuple[bool, re.Pattern[str]]]:
# First group negative-positive expressions.
pattern_groups: List[Tuple[bool, List[str]]] = []
for pattern in path_patterns:
if pattern.startswith("!"):
pattern = pattern.lstrip("!")
negate = True
else:
negate = False
if not pattern:
continue
if not pattern_groups:
pattern_groups.append((negate, []))
pattern_regex = PathPatternMatch._pattern_match_as_regex_single(pattern)
if pattern_groups[-1][0] == negate:
pattern_groups[-1][1].append(pattern_regex)
else:
pattern_groups.append((negate, [pattern_regex]))
result: List[Tuple[bool, re.Pattern[str]]] = []
for negate, pattern_list in pattern_groups:
result.append((negate, re.compile("(?:{:s})".format("|".join(pattern_list)), re.MULTILINE)))
# print(result)
return result
# -----------------------------------------------------------------------------
# URL Downloading
@ -976,6 +1258,47 @@ def pkg_manifest_validate_field_permissions(
return None
def pkg_manifest_validate_field_build_path_list(value: List[Any], strict: bool) -> Optional[str]:
_ = strict
value_duplicate_check: Set[str] = set()
for item in value:
if not isinstance(item, str):
return "Expected \"paths\" to be a list of strings, found {:s}".format(str(type(item)))
if not item:
return "Expected \"paths\" items to be a non-empty string"
if "\\" in item:
return "Expected \"paths\" items to use \"/\" slashes, found: {:s}".format(item)
if "\n" in item:
return "Expected \"paths\" items to contain single lines, found: {:s}".format(item)
# TODO: properly handle WIN32 absolute paths.
if item.startswith("/"):
return "Expected \"paths\" to be relative, found: {:s}".format(item)
# Disallow references to `../../path` as this wont map into a the archive properly.
# Further it may provide a security problem.
item_native = os.path.normpath(item if os.sep == "/" else item.replace("/", "\\"))
if item_native.startswith(".." + os.sep):
return "Expected \"paths\" items to reference paths within a directory, found: {:s}".format(item)
# Disallow duplicate names (when lower-case) to avoid collisions on case insensitive file-systems.
item_native_lower = item_native.lower()
len_prev = len(value_duplicate_check)
value_duplicate_check.add(item_native_lower)
if len_prev == len(value_duplicate_check):
return "Expected \"paths\" to contain unique paths, duplicate found: {:s}".format(item)
# Having to support this optionally ends up being reasonably complicated.
# Simply throw an error if it's included, so it can be added at build time.
if item_native == PKG_MANIFEST_FILENAME_TOML:
return "Expected \"paths\" not to contain the manifest, found: {:s}".format(item)
# NOTE: other checks could be added here, (exclude control characters for example).
# Such cases are quite unlikely so supporting them isn't so important.
return None
def pkg_manifest_validate_field_wheels(
value: List[Any],
strict: bool,
@ -2199,12 +2522,69 @@ class subcmd_author:
message_error(msg_fn, "File \"{:s}\" not found!".format(pkg_manifest_filepath))
return False
manifest = pkg_manifest_from_toml_and_validate_all_errors(pkg_manifest_filepath, strict=True)
# TODO: don't use this line, because the build information needs to be extracted too.
# This should be refactored so the manifest could *optionally* load `[build]` info.
# `manifest = pkg_manifest_from_toml_and_validate_all_errors(pkg_manifest_filepath, strict=True)`
try:
with open(pkg_manifest_filepath, "rb") as fh:
manifest_data = tomllib.load(fh)
except Exception as ex:
message_error(msg_fn, "Error parsing TOML \"{:s}\" {:s}".format(pkg_manifest_filepath, str(ex)))
return False
manifest = pkg_manifest_from_dict_and_validate_all_errros(manifest_data, from_repo=False, strict=True)
if isinstance(manifest, list):
for error_msg in manifest:
message_error(msg_fn, "Error parsing TOML \"{:s}\" {:s}".format(pkg_manifest_filepath, error_msg))
return False
if (manifest_build_data := manifest_data.get("build")) is not None:
manifest_build_test = PkgManifest_Build.from_dict_all_errors(manifest_build_data, extra_paths=[
# Inclusion of the manifest is implicit.
# No need to require the manifest to include itself.
PKG_MANIFEST_FILENAME_TOML,
*(manifest.wheels or ()),
])
if isinstance(manifest_build_test, list):
for error_msg in manifest_build_test:
message_error(msg_fn, "Error parsing TOML \"{:s}\" {:s}".format(pkg_manifest_filepath, error_msg))
return False
manifest_build = manifest_build_test
del manifest_build_test
else:
# Make default build options if none are provided.
manifest_build = PkgManifest_Build(
paths=None,
paths_exclude_pattern=[
"__pycache__/",
".*",
],
)
del manifest_build_data, manifest_data
build_paths_exclude_pattern: Optional[PathPatternMatch] = None
if manifest_build.paths_exclude_pattern is not None:
build_paths_exclude_pattern = PathPatternMatch(manifest_build.paths_exclude_pattern)
build_paths: Optional[List[str]] = None
if manifest_build.paths is not None:
build_paths = manifest_build.paths
def scandir_filter_with_paths_exclude_pattern(filepath: str, is_dir: bool) -> bool:
assert build_paths_exclude_pattern is not None
if os.sep == "\\":
filepath = filepath.replace("\\", "/")
if is_dir:
assert not filepath.endswith("/")
filepath = filepath + "/"
assert not filepath.startswith(("/", "./", "../"))
return not build_paths_exclude_pattern.test_path(filepath)
def scandir_filter_fallback(filepath: str, is_dir: bool) -> bool:
_ = is_dir
return not os.path.basename(filepath).startswith(".")
pkg_filename = manifest.id + PKG_EXT
if pkg_output_filepath != "":
@ -2219,11 +2599,9 @@ class subcmd_author:
# It's possible a temporary file exists from a previous run which was not cleaned up.
# Although in general this should be cleaned up - power failure etc may mean it exists.
pkg_filename + "@",
# This is added, converted from the TOML.
PKG_REPO_LIST_FILENAME,
# We could exclude the manifest: `PKG_MANIFEST_FILENAME_TOML`
# but it's now used so a generation step isn't needed.
# Keep the `PKG_MANIFEST_FILENAME_TOML` as this is used when installing packages
# to a users local repository, where there is no `PKG_REPO_LIST_FILENAME` to access the meta-data.
}
request_exit = False
@ -2240,11 +2618,20 @@ class subcmd_author:
return False
with contextlib.closing(zip_fh_context) as zip_fh:
for filepath_abs, filepath_rel in scandir_recursive(
if build_paths is not None:
filepath_iterator = build_paths_expand_iter(pkg_source_dir, build_paths)
else:
filepath_iterator = scandir_recursive(
pkg_source_dir,
# Be more advanced in the future, for now ignore dot-files (`.git`) .. etc.
filter_fn=lambda x: not x.startswith(".")
):
filter_fn=(
scandir_filter_with_paths_exclude_pattern if build_paths_exclude_pattern else
# In this case there isn't really a good option, just ignore all dot-files.
scandir_filter_fallback
),
)
for filepath_abs, filepath_rel in filepath_iterator:
if filepath_rel in filenames_root_exclude:
continue

View File

@ -15,3 +15,11 @@ license = [
copyright = [
"Developer Name"
]
[build]
paths_exclude_pattern = [
# Exclude all Python cache.
"__pycache__/",
# Exclude `.git` and similar.
".*",
]

View File

@ -0,0 +1,182 @@
# SPDX-FileCopyrightText: 2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
"""
This test calls into the path pattern matching logic of ``blender_ext`` directly.
"""
import unittest
import os
from typing import (
Any,
Dict,
List,
Sequence,
Tuple,
Union,
)
CURRENT_DIR = os.path.abspath(os.path.dirname(__file__))
BASE_DIR = os.path.normpath(os.path.join(CURRENT_DIR, ".."))
# Don't import as module, instead load the class.
def execfile(filepath: str, *, name: str = "__main__") -> Dict[str, Any]:
global_namespace = {"__file__": filepath, "__name__": name}
with open(filepath, encoding="utf-8") as file_handle:
exec(compile(file_handle.read(), filepath, 'exec'), global_namespace)
return global_namespace
PathPatternMatch = execfile(os.path.join(BASE_DIR, "cli", "blender_ext.py"), name="blender_ext")["PathPatternMatch"]
assert isinstance(PathPatternMatch, type)
class TestPathMatch_MixIn:
def match_paths(
self,
expected_paths: List[Tuple[bool, str]],
path_pattern: Union[Sequence[str], PathPatternMatch], # type: ignore
) -> List[Tuple[bool, str]]:
result = []
if not isinstance(path_pattern, PathPatternMatch):
path_pattern = PathPatternMatch(path_pattern)
assert hasattr(path_pattern, "test_path")
for success_expected, path in expected_paths:
success_actual = path_pattern.test_path(path)
if False:
self.assertEqual(success_actual, success_expected)
result.append((success_actual, path))
return result
def match_paths_for_cmp(
self,
expected_paths: List[Tuple[bool, str]],
path_pattern: Union[Sequence[str], PathPatternMatch], # type: ignore
) -> Tuple[
List[Tuple[bool, str]],
List[Tuple[bool, str]],
]:
return self.match_paths(expected_paths, path_pattern), expected_paths
class TestSingle(unittest.TestCase, TestPathMatch_MixIn):
def test_directory(self) -> None:
pattern = PathPatternMatch(["__pycache__/"])
self.assertEqual(*self.match_paths_for_cmp([(True, "__pycache__/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a/__pycache__/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a/__pycache__/any")], pattern))
# Not a directory.
self.assertEqual(*self.match_paths_for_cmp([(False, "__pycache__")], pattern))
# Not exact matches.
self.assertEqual(*self.match_paths_for_cmp([(False, "__pycache_/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "__pycache___/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "___pycache__/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "_pycache__/")], pattern))
# Not exact matches (sub-directory).
self.assertEqual(*self.match_paths_for_cmp([(False, "a/__pycache_/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/__pycache___/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/___pycache__/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/_pycache__/")], pattern))
def test_file_extension(self) -> None:
pattern = PathPatternMatch(["*.pyc"])
self.assertEqual(*self.match_paths_for_cmp([(True, ".pyc")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a.pyc")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a/.pyc")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a/a.pyc")], pattern))
# Not exact matches.
self.assertEqual(*self.match_paths_for_cmp([(False, ".pycx")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a.pycx")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, ".pyc/x")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a.pyc/x")], pattern))
# Not exact matches (sub-directory).
self.assertEqual(*self.match_paths_for_cmp([(False, "a/.pycx")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/a.pycx")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/.pyc/x")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/a.pyc/x")], pattern))
def test_double_star(self) -> None:
pattern = PathPatternMatch(["a/**/b"])
self.assertEqual(*self.match_paths_for_cmp([(True, "a/x/b")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a/x/y/z/b")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a/b")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a_/x/b")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a_/x/y/z/b")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a_/b")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/x/_b")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/x/y/z/_b")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/_b")], pattern))
def test_double_star_directory(self) -> None:
pattern = PathPatternMatch(["a/**/b/"])
self.assertEqual(*self.match_paths_for_cmp([(True, "a/x/b/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a/x/y/z/b/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "a/b/")], pattern))
def test_root_directory(self) -> None:
pattern = PathPatternMatch(["/build"])
self.assertEqual(*self.match_paths_for_cmp([(True, "build/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "build")], pattern))
# Not absolute.
self.assertEqual(*self.match_paths_for_cmp([(False, "a/build/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "a/build")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "build_/")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "build_")], pattern))
def test_root_directory_file(self) -> None:
pattern = PathPatternMatch(["/a/build.txt"])
self.assertEqual(*self.match_paths_for_cmp([(True, "a/build.txt")], pattern))
# Not root relative.
self.assertEqual(*self.match_paths_for_cmp([(False, "b/a/build.txt")], pattern))
def test_root_directory_file_star(self) -> None:
pattern = PathPatternMatch(["/test/*.txt"])
self.assertEqual(*self.match_paths_for_cmp([(True, "test/a.txt")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "test/b/c.txt")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "b/test/c.txt")], pattern))
class TestMulti(unittest.TestCase, TestPathMatch_MixIn):
def test_negate(self) -> None:
pattern = PathPatternMatch([
"data.*",
"!data.csv",
])
self.assertEqual(*self.match_paths_for_cmp([(True, "test/data.txt")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "test/data.csv")], pattern))
def test_negate_spesific(self) -> None:
pattern = PathPatternMatch([
"data.*",
"!/test/data.csv",
])
self.assertEqual(*self.match_paths_for_cmp([(True, "other/data.csv")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(True, "other/data.txt")], pattern))
self.assertEqual(*self.match_paths_for_cmp([(False, "test/data.csv")], pattern))
if __name__ == "__main__":
unittest.main()

View File

@ -26,9 +26,15 @@ def register() -> None:
# Click to apply pose.
kmi = km.keymap_items.new("poselib.apply_pose_asset", "LEFTMOUSE", "CLICK")
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.apply_pose_asset", "LEFTMOUSE", "CLICK", ctrl=True)
kmi.properties.flipped = True
addon_keymaps.append((km, kmi))
# Drag to blend pose.
kmi = km.keymap_items.new("poselib.blend_pose_asset", "LEFTMOUSE", "CLICK_DRAG")
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.blend_pose_asset", "LEFTMOUSE", "CLICK_DRAG", ctrl=True)
kmi.properties.flipped = True
addon_keymaps.append((km, kmi))
def unregister() -> None:

View File

@ -27,9 +27,8 @@ class SpaceAssetInfo:
@classmethod
def get_active_asset(cls, context: Context):
if hasattr(context, "active_file"):
active_file = context.active_file
return active_file.asset_data if active_file else None
if active_file := getattr(context, "active_file", None):
return active_file.asset_data
class AssetBrowserPanel:

View File

@ -611,7 +611,7 @@ def _template_items_tool_select(
else:
# For right mouse, set the cursor.
return [
(cursor_operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
(cursor_operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, None) if cursor_operator is not None else (),
("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("release_confirm", True), ("cursor_transform", True)]}),
]
@ -4792,7 +4792,7 @@ def km_grease_pencil_fill_tool(_params):
("grease_pencil.fill", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("on_back", False)]}),
("grease_pencil.fill", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("on_back", False)]}),