Refactor: combine insert_keyframe() and insert_key_rna() into a single function #122053
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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 \
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -15,3 +15,11 @@ license = [
|
||||
copyright = [
|
||||
"Developer Name"
|
||||
]
|
||||
|
||||
[build]
|
||||
paths_exclude_pattern = [
|
||||
# Exclude all Python cache.
|
||||
"__pycache__/",
|
||||
# Exclude `.git` and similar.
|
||||
".*",
|
||||
]
|
||||
|
182
scripts/addons_core/bl_pkg/tests/test_path_pattern_match.py
Normal file
182
scripts/addons_core/bl_pkg/tests/test_path_pattern_match.py
Normal 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()
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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)]}),
|
||||
|