UV Constrain to bounds not constraining when scaling UV's
Open, ConfirmedPublic

Description

System: MacOS 10.12

Blender Version: 2.78a

Issue: UV's are able to be scaled beyond bounds in 'constrain to bounds mode' when pivot point is set to 2D cursor, and positioned on an edge. (possibly other scenarios as well- however that is what I have experimented with)

This issue can also be reproduced on the x axis as well, it seems to be dependent on where the 2D cursor is placed within the UV workspace.

Details

Type
Bug

Related Objects

Alex (Alexmitchellmus) changed the title from "UV Constrain to bounds not constrining when scaling UV's" to "UV Constrain to bounds not constraining when scaling UV's".
Alex (Alexmitchellmus) edited the task description. (Show Details)
Philipp Oeser (lichtwerk) triaged this task as "Confirmed" priority.Jan 11 2017, 10:54 PM

first note: seems to only happen when 2d cursor is exactly on the bounds (or outside)
Will check logic in clipUVTransform() when time permits [could take some time though due to dayjob... so if somebody wants to jump in....]

I had a look at this just yesterday and have this working mostly already, only have to iron out a cornercase...
So assigning this to me [as I can finish it up this weekend] and hope you didnt already work on this.
If you already solved this though in the meantime feel free to comandeer back...

Hi there,
unfortunately I didnt find the time to finish/clean this up (and I am not sure when I will find time) so I'm just pasting a very rough diff for others to pick up....

1
2
3diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
4--- a/source/blender/editors/transform/transform.c
5+++ b/source/blender/editors/transform/transform.c
6@@ -3453,6 +3453,8 @@ static void applyResize(TransInfo *t, const int mval[2])
7
8​ /* evil hack - redo resize if cliping needed */
9​ if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values, 1)) {
10+ headerResize(t, t->values, str);
11+
12​ size_to_mat3(mat, t->values);
13
14​ if (t->con.applySize)
15diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
16--- a/source/blender/editors/transform/transform_conversions.c
17+++ b/source/blender/editors/transform/transform_conversions.c
18@@ -2955,46 +2955,107 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
19​ {
20​ TransData *td;
21​ int a;
22- bool clipx = true, clipy = true;
23- float min[2], max[2];
24+ bool clipx = false, clipy = false;
25+ bool sclipx[2], sclipy[2];
26+ float min[2], max[2], median[2], scale_compensate[2][2];
27
28+ sclipx[0] = sclipx[1] = sclipy[0] = sclipy[1] = false;
29+ scale_compensate[0][0] = scale_compensate[0][1] = scale_compensate[1][0] = scale_compensate[1][1] = 1.0f;
30+ median[0] = median[1] = 0.0f;
31​ min[0] = min[1] = 0.0f;
32​ max[0] = t->aspect[0];
33​ max[1] = t->aspect[1];
34
35​ for (a = 0, td = t->data; a < t->total; a++, td++) {
36​ minmax_v2v2_v2(min, max, td->loc);
37+ add_v2_v2(median, td->iloc);
38​ }
39+ mul_v2_fl(median, (1.0f / (float)t->total));
40+
41+ // still need to solve the case when scaling vec[0] or vec[1] is negative and cursor is outside of bounds
42
43​ if (resize) {
44- if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < t->aspect[0] * 0.5f)
45- vec[0] *= t->center[0] / (t->center[0] - min[0]);
46- else if (max[0] > t->aspect[0] && t->center[0] < t->aspect[0])
47- vec[0] *= (t->center[0] - t->aspect[0]) / (t->center[0] - max[0]);
48- else
49- clipx = 0;
50+ if (median[0] < 0.0f || median[0] > t->aspect[0] || median[1] < 0.0f || median[1] > t->aspect[1]) {
51+ return false;
52+ }
53
54- if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < t->aspect[1] * 0.5f)
55- vec[1] *= t->center[1] / (t->center[1] - min[1]);
56- else if (max[1] > t->aspect[1] && t->center[1] < t->aspect[1])
57- vec[1] *= (t->center[1] - t->aspect[1]) / (t->center[1] - max[1]);
58- else
59- clipy = 0;
60+ if (min[0] < 0.0f) {
61+ /* could run into devide by zero? */
62+ scale_compensate[0][0] = t->center[0] / (t->center[0] - min[0]);
63+ sclipx[0] = true;
64+ }
65+ if (max[0] > t->aspect[0]) {
66+ scale_compensate[0][1] = (t->center[0] - t->aspect[0]) / (t->center[0] - max[0]);
67+ sclipx[1] = true;
68+ }
69+ if (sclipx[0] || sclipx[1]) {
70+ if (sclipx[0] && !sclipx[1]) {
71+ vec[0] *= scale_compensate[0][0];
72+ }
73+ else if (sclipx[1] && !sclipx[0]) {
74+ vec[0] *= scale_compensate[0][1];
75+ }
76+ else {
77+ if ((abs(1.0f - scale_compensate[0][0]) > abs(1.0f - scale_compensate[0][1])) && sclipx[0]) {
78+ vec[0] *= scale_compensate[0][0];
79+ }
80+ else {
81+ vec[0] *= scale_compensate[0][1];
82+ }
83+ }
84+ clipx = true;
85+ }
86+
87+ if (min[1] < 0.0f) {
88+ scale_compensate[1][0] = t->center[1] / (t->center[1] - min[1]);
89+ sclipy[0] = true;
90+ }
91+ if (max[1] > t->aspect[1]) {
92+ scale_compensate[1][1] = (t->center[1] - t->aspect[1]) / (t->center[1] - max[1]);
93+ sclipy[1] = true;
94+ }
95+ if (sclipy[0] || sclipy[1]) {
96+ if (sclipy[0] && !sclipy[1]) {
97+ vec[1] *= scale_compensate[1][0];
98+ }
99+ else if (sclipy[1] && !sclipy[0]) {
100+ vec[1] *= scale_compensate[1][1];
101+ }
102+ else {
103+ if ((abs(1.0f - scale_compensate[1][0]) > abs(1.0f - scale_compensate[1][1])) && sclipy[0]) {
104+ vec[1] *= scale_compensate[1][0];
105+ }
106+ else {
107+ vec[1] *= scale_compensate[1][1];
108+ }
109+ }
110+ clipy = true;
111+ }
112+
113+ /* just idea to keep scaling uniform in aspect
114+ if (sclipx[0] || sclipx[1] || sclipy[0] || sclipy[1]) {
115+ mul_v2_fl(vec, min_ffff(scale_compensate[0][0], scale_compensate[0][1], scale_compensate[1][0], scale_compensate[1][1]));
116+ }
117+ */
118​ }
119​ else {
120- if (min[0] < 0.0f)
121+ if (min[0] < 0.0f) {
122​ vec[0] -= min[0];
123- else if (max[0] > t->aspect[0])
124+ clipx = true;
125+ }
126+ else if (max[0] > t->aspect[0]) {
127​ vec[0] -= max[0] - t->aspect[0];
128- else
129- clipx = 0;
130+ clipx = true;
131+ }
132
133- if (min[1] < 0.0f)
134+ if (min[1] < 0.0f) {
135​ vec[1] -= min[1];
136- else if (max[1] > t->aspect[1])
137+ clipy = true;
138+ }
139+ if (max[1] > t->aspect[1]) {
140​ vec[1] -= max[1] - t->aspect[1];
141- else
142- clipy = 0;
143+ clipy = true;
144+ }
145​ }
146
147​ return (clipx || clipy);

This just tries to solve a couple of issues I found with the clipping code:

  • clip minx/miny not working (if center/cursor is 'in right side')
  • clip doesnt allow for cursor being outside of UV bounds (or directly on the bounds like in this report) [the paste allows for this]
  • when scaling and runnning into clipmax first, then scale further and run into clipmin after --> pop to clipmin
  • also wasnt sure if it is a good idea to scale further on other axis after running into one clipping border [making the scale non-uniform in aspect] - which is the case in current code. There is a small snippet in the paste that could prevent this

This is obviously very rough and could propably be simplified [its more like working pseudo code...]

Wow Philipp! Really fantastic start.

In regard to non-uniform scaling (changing scaling aspect ratio) - maybe this could be a menu item? As I can think of usage cases where I may want to achieve both.

Thanks.