1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.constraintlayout.core; 18 19 import java.util.Arrays; 20 import java.util.Comparator; 21 22 /** 23 * Implements a row containing goals taking in account priorities. 24 */ 25 public class PriorityGoalRow extends ArrayRow { 26 private static final float EPSILON = 0.0001f; 27 @SuppressWarnings("unused") private static final boolean DEBUG = false; 28 29 private int mTableSize = 128; 30 private SolverVariable[] mArrayGoals = new SolverVariable[mTableSize]; 31 private SolverVariable[] mSortArray = new SolverVariable[mTableSize]; 32 private int mNumGoals = 0; 33 GoalVariableAccessor mAccessor = new GoalVariableAccessor(this); 34 35 class GoalVariableAccessor { 36 SolverVariable mVariable; 37 PriorityGoalRow mRow; 38 GoalVariableAccessor(PriorityGoalRow row)39 GoalVariableAccessor(PriorityGoalRow row) { 40 this.mRow = row; 41 } 42 init(SolverVariable variable)43 public void init(SolverVariable variable) { 44 this.mVariable = variable; 45 } 46 addToGoal(SolverVariable other, float value)47 public boolean addToGoal(SolverVariable other, float value) { 48 if (mVariable.inGoal) { 49 boolean empty = true; 50 for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) { 51 mVariable.mGoalStrengthVector[i] += other.mGoalStrengthVector[i] * value; 52 float v = mVariable.mGoalStrengthVector[i]; 53 if (Math.abs(v) < EPSILON) { 54 mVariable.mGoalStrengthVector[i] = 0; 55 } else { 56 empty = false; 57 } 58 } 59 if (empty) { 60 removeGoal(mVariable); 61 } 62 } else { 63 for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) { 64 float strength = other.mGoalStrengthVector[i]; 65 if (strength != 0) { 66 float v = value * strength; 67 if (Math.abs(v) < EPSILON) { 68 v = 0; 69 } 70 mVariable.mGoalStrengthVector[i] = v; 71 } else { 72 mVariable.mGoalStrengthVector[i] = 0; 73 } 74 } 75 return true; 76 } 77 return false; 78 } 79 add(SolverVariable other)80 public void add(SolverVariable other) { 81 for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) { 82 mVariable.mGoalStrengthVector[i] += other.mGoalStrengthVector[i]; 83 float value = mVariable.mGoalStrengthVector[i]; 84 if (Math.abs(value) < EPSILON) { 85 mVariable.mGoalStrengthVector[i] = 0; 86 } 87 } 88 } 89 isNegative()90 public final boolean isNegative() { 91 for (int i = SolverVariable.MAX_STRENGTH - 1; i >= 0; i--) { 92 float value = mVariable.mGoalStrengthVector[i]; 93 if (value > 0) { 94 return false; 95 } 96 if (value < 0) { 97 return true; 98 } 99 } 100 return false; 101 } 102 isSmallerThan(SolverVariable other)103 public final boolean isSmallerThan(SolverVariable other) { 104 for (int i = SolverVariable.MAX_STRENGTH - 1; i >= 0; i--) { 105 float comparedValue = other.mGoalStrengthVector[i]; 106 float value = mVariable.mGoalStrengthVector[i]; 107 if (value == comparedValue) { 108 continue; 109 } 110 return value < comparedValue; 111 } 112 return false; 113 } 114 isNull()115 public final boolean isNull() { 116 for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) { 117 if (mVariable.mGoalStrengthVector[i] != 0) { 118 return false; 119 } 120 } 121 return true; 122 } 123 reset()124 public void reset() { 125 Arrays.fill(mVariable.mGoalStrengthVector, 0); 126 } 127 128 @Override toString()129 public String toString() { 130 String result = "[ "; 131 if (mVariable != null) { 132 for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) { 133 result += mVariable.mGoalStrengthVector[i] + " "; 134 } 135 } 136 result += "] " + mVariable; 137 return result; 138 } 139 140 } 141 142 @Override clear()143 public void clear() { 144 mNumGoals = 0; 145 mConstantValue = 0; 146 } 147 148 Cache mCache; 149 PriorityGoalRow(Cache cache)150 public PriorityGoalRow(Cache cache) { 151 super(cache); 152 mCache = cache; 153 } 154 155 @Override isEmpty()156 public boolean isEmpty() { 157 return mNumGoals == 0; 158 } 159 160 static final int NOT_FOUND = -1; 161 162 @Override getPivotCandidate(LinearSystem system, boolean[] avoid)163 public SolverVariable getPivotCandidate(LinearSystem system, boolean[] avoid) { 164 int pivot = NOT_FOUND; 165 for (int i = 0; i < mNumGoals; i++) { 166 SolverVariable variable = mArrayGoals[i]; 167 if (avoid[variable.id]) { 168 continue; 169 } 170 mAccessor.init(variable); 171 if (pivot == NOT_FOUND) { 172 if (mAccessor.isNegative()) { 173 pivot = i; 174 } 175 } else if (mAccessor.isSmallerThan(mArrayGoals[pivot])) { 176 pivot = i; 177 } 178 } 179 if (pivot == NOT_FOUND) { 180 return null; 181 } 182 return mArrayGoals[pivot]; 183 } 184 185 @Override addError(SolverVariable error)186 public void addError(SolverVariable error) { 187 mAccessor.init(error); 188 mAccessor.reset(); 189 error.mGoalStrengthVector[error.strength] = 1; 190 addToGoal(error); 191 } 192 addToGoal(SolverVariable variable)193 private void addToGoal(SolverVariable variable) { 194 if (mNumGoals + 1 > mArrayGoals.length) { 195 mArrayGoals = Arrays.copyOf(mArrayGoals, mArrayGoals.length * 2); 196 mSortArray = Arrays.copyOf(mArrayGoals, mArrayGoals.length * 2); 197 } 198 mArrayGoals[mNumGoals] = variable; 199 mNumGoals++; 200 201 if (mNumGoals > 1 && mArrayGoals[mNumGoals - 1].id > variable.id) { 202 for (int i = 0; i < mNumGoals; i++) { 203 mSortArray[i] = mArrayGoals[i]; 204 } 205 Arrays.sort(mSortArray, 0, mNumGoals, new Comparator<SolverVariable>() { 206 @Override 207 public int compare(SolverVariable variable1, SolverVariable variable2) { 208 return variable1.id - variable2.id; 209 } 210 }); 211 for (int i = 0; i < mNumGoals; i++) { 212 mArrayGoals[i] = mSortArray[i]; 213 } 214 } 215 216 variable.inGoal = true; 217 variable.addToRow(this); 218 } 219 removeGoal(SolverVariable variable)220 private void removeGoal(SolverVariable variable) { 221 for (int i = 0; i < mNumGoals; i++) { 222 if (mArrayGoals[i] == variable) { 223 for (int j = i; j < mNumGoals - 1; j++) { 224 mArrayGoals[j] = mArrayGoals[j + 1]; 225 } 226 mNumGoals--; 227 variable.inGoal = false; 228 return; 229 } 230 } 231 } 232 233 @Override updateFromRow(LinearSystem system, ArrayRow definition, boolean removeFromDefinition)234 public void updateFromRow(LinearSystem system, 235 ArrayRow definition, 236 boolean removeFromDefinition) { 237 SolverVariable goalVariable = definition.mVariable; 238 if (goalVariable == null) { 239 return; 240 } 241 242 ArrayRowVariables rowVariables = definition.variables; 243 int currentSize = rowVariables.getCurrentSize(); 244 for (int i = 0; i < currentSize; i++) { 245 SolverVariable solverVariable = rowVariables.getVariable(i); 246 float value = rowVariables.getVariableValue(i); 247 mAccessor.init(solverVariable); 248 if (mAccessor.addToGoal(goalVariable, value)) { 249 addToGoal(solverVariable); 250 } 251 mConstantValue += definition.mConstantValue * value; 252 } 253 removeGoal(goalVariable); 254 } 255 256 @Override toString()257 public String toString() { 258 String result = ""; 259 result += " goal -> (" + mConstantValue + ") : "; 260 for (int i = 0; i < mNumGoals; i++) { 261 SolverVariable v = mArrayGoals[i]; 262 mAccessor.init(v); 263 result += mAccessor + " "; 264 } 265 return result; 266 } 267 } 268