| 1 | color BLACK = #000000; |
| 2 | |
| 3 | class Gimbal extends SCPattern { |
| 4 | |
| 5 | private final boolean DEBUG_MANUAL_ABG = false; |
| 6 | private final int MAXIMUM_BEATS_PER_REVOLUTION = 100; |
| 7 | |
| 8 | private boolean first_run = true; |
| 9 | private final Projection projection; |
| 10 | private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 20./MAXIMUM_BEATS_PER_REVOLUTION); |
| 11 | private final BasicParameter hueDeltaParam = new BasicParameter("HUED", 60./360); |
| 12 | private final BasicParameter fadeFromCoreParam = new BasicParameter("FADE", 1); |
| 13 | private final BasicParameter girthParam = new BasicParameter("GRTH", .18); |
| 14 | private final BasicParameter ringExtendParam = new BasicParameter("XTND", 1); |
| 15 | private final BasicParameter relativeSpeedParam = new BasicParameter("RLSP", .83); |
| 16 | private final BasicParameter sizeParam = new BasicParameter("SIZE", .9); |
| 17 | |
| 18 | private final BasicParameter aP = new BasicParameter("a", 0); |
| 19 | private final BasicParameter bP = new BasicParameter("b", 0); |
| 20 | private final BasicParameter gP = new BasicParameter("g", 0); |
| 21 | |
| 22 | Gimbal(GLucose glucose) { |
| 23 | super(glucose); |
| 24 | projection = new Projection(model); |
| 25 | addParameter(beatsPerRevolutionParam); |
| 26 | addParameter(hueDeltaParam); |
| 27 | addParameter(fadeFromCoreParam); |
| 28 | addParameter(girthParam); |
| 29 | addParameter(ringExtendParam); |
| 30 | addParameter(relativeSpeedParam); |
| 31 | addParameter(sizeParam); |
| 32 | |
| 33 | if (DEBUG_MANUAL_ABG) { |
| 34 | addParameter(aP); |
| 35 | addParameter(bP); |
| 36 | addParameter(gP); |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | float a = 0, b = 0, g = 0; |
| 41 | |
| 42 | public void run(double deltaMs) { |
| 43 | |
| 44 | if (DEBUG_MANUAL_ABG) { |
| 45 | a = aP.getValuef() * (2 * PI); |
| 46 | b = bP.getValuef() * (2 * PI); |
| 47 | g = gP.getValuef() * (2 * PI); |
| 48 | } else { |
| 49 | float relativeSpeed = relativeSpeedParam.getValuef(); |
| 50 | float time = millis() / 1000.f; |
| 51 | |
| 52 | int beatsPerRevolution = (int) (beatsPerRevolutionParam.getValuef() * MAXIMUM_BEATS_PER_REVOLUTION) + 1; |
| 53 | float radiansPerMs = 2 * PI // radians / revolution |
| 54 | / beatsPerRevolution // beats / revolution |
| 55 | * lx.tempo.bpmf() // BPM beats / min |
| 56 | / 60 // sec / min |
| 57 | / 1000; // ms / sec |
| 58 | |
| 59 | a += deltaMs * radiansPerMs * pow(relativeSpeed, 0); |
| 60 | b += deltaMs * radiansPerMs * pow(relativeSpeed, 1); |
| 61 | g += deltaMs * radiansPerMs * pow(relativeSpeed, 2); |
| 62 | a %= 2 * PI; |
| 63 | b %= 2 * PI; |
| 64 | g %= 2 * PI; |
| 65 | } |
| 66 | |
| 67 | float hue = lx.getBaseHuef(); |
| 68 | float hue_delta = hueDeltaParam.getValuef() * 360; |
| 69 | |
| 70 | float radius1 = model.xMax / 2 * sizeParam.getValuef(); |
| 71 | float radius2 = ((model.xMax + model.yMax) / 2) / 2 * sizeParam.getValuef(); |
| 72 | float radius3 = model.yMax / 2 * sizeParam.getValuef(); |
| 73 | float girth = model.xMax * girthParam.getValuef(); |
| 74 | Ring ring1 = new Ring((hue + hue_delta * 0) % 360, radius1, girth); |
| 75 | Ring ring2 = new Ring((hue + hue_delta * 1) % 360, radius2, girth); |
| 76 | Ring ring3 = new Ring((hue + hue_delta * 2) % 360, radius3, girth); |
| 77 | |
| 78 | projection.reset(model) |
| 79 | // Translate so the center of the car is the origin |
| 80 | .translateCenter(model, 0, 0, 0); |
| 81 | |
| 82 | for (Coord c : projection) { |
| 83 | //if (first_run) println(c.x + "," + c.y + "," + c.z); |
| 84 | |
| 85 | rotate3d(c, a, 0, 0); |
| 86 | rotate3d(c, PI/4, PI/4, PI/4); |
| 87 | color color1 = ring1.colorFor(c); |
| 88 | |
| 89 | rotate3d(c, 0, b, 0); |
| 90 | color color2 = ring2.colorFor(c); |
| 91 | |
| 92 | rotate3d(c, 0, 0, g); |
| 93 | color color3 = ring3.colorFor(c); |
| 94 | |
| 95 | colors[c.index] = specialBlend(color1, color2, color3); |
| 96 | } |
| 97 | |
| 98 | first_run = false; |
| 99 | } |
| 100 | |
| 101 | class Ring { |
| 102 | |
| 103 | float hue; |
| 104 | float radius, girth; |
| 105 | |
| 106 | public Ring(float hue, float radius, float girth) { |
| 107 | this.hue = hue; |
| 108 | this.radius = radius; |
| 109 | this.girth = girth; |
| 110 | } |
| 111 | |
| 112 | public color colorFor(Coord c) { |
| 113 | float theta = atan2(c.y, c.x); |
| 114 | float nearest_circle_x = cos(theta) * radius; |
| 115 | float nearest_circle_y = sin(theta) * radius; |
| 116 | float nearest_circle_z = 0; |
| 117 | |
| 118 | float distance_to_circle |
| 119 | = sqrt(pow(nearest_circle_x - c.x, 2) |
| 120 | + pow(nearest_circle_y - c.y, 2) |
| 121 | + pow(nearest_circle_z - c.z * ringExtendParam.getValuef(), 2)); |
| 122 | |
| 123 | float xy_distance = sqrt(c.x*c.x + c.y*c.y); |
| 124 | return lx.hsb(this.hue, 100, (1 - distance_to_circle / girth * fadeFromCoreParam.getValuef()) * 100); |
| 125 | } |
| 126 | |
| 127 | } |
| 128 | |
| 129 | } |
| 130 | |
| 131 | |
| 132 | |
| 133 | |
| 134 | |
| 135 | |
| 136 | class Zebra extends SCPattern { |
| 137 | |
| 138 | private final Projection projection; |
| 139 | SinLFO angleM = new SinLFO(0, PI * 2, 30000); |
| 140 | |
| 141 | /* |
| 142 | SinLFO x, y, z, dx, dy, dz; |
| 143 | float cRad; |
| 144 | _P size; |
| 145 | */ |
| 146 | |
| 147 | Zebra(GLucose glucose) { |
| 148 | super(glucose); |
| 149 | projection = new Projection(model); |
| 150 | |
| 151 | addModulator(angleM).trigger(); |
| 152 | } |
| 153 | |
| 154 | color colorFor(Coord c) { |
| 155 | float hue = lx.getBaseHuef(); |
| 156 | |
| 157 | |
| 158 | |
| 159 | |
| 160 | /* SLIDE ALONG |
| 161 | c.x = c.x + millis() / 100.f; |
| 162 | */ |
| 163 | |
| 164 | |
| 165 | |
| 166 | int stripe_count = 12; |
| 167 | float stripe_width = model.xMax / (float)stripe_count; |
| 168 | if (Math.floor((c.x) / stripe_width) % 2 == 0) { |
| 169 | return lx.hsb(hue, 100, 100); |
| 170 | } else { |
| 171 | return lx.hsb((hue + 90) % 360, 100, 100); |
| 172 | } |
| 173 | |
| 174 | |
| 175 | /* OCTANTS |
| 176 | |
| 177 | if ((isPositiveBit(c.x) + isPositiveBit(c.y) + isPositiveBit(c.z)) % 2 == 0) { |
| 178 | return lx.hsb(lx.getBaseHuef(), 100, 100); |
| 179 | } else { |
| 180 | return lx.hsb(0, 0, 0); |
| 181 | } |
| 182 | */ |
| 183 | } |
| 184 | |
| 185 | int isPositiveBit(float f) { |
| 186 | return f > 0 ? 1 : 0; |
| 187 | } |
| 188 | |
| 189 | public void run(double deltaMs) { |
| 190 | float a = (millis() / 1000.f) % (2 * PI); |
| 191 | float b = (millis() / 1200.f) % (2 * PI); |
| 192 | float g = (millis() / 1600.f) % (2 * PI); |
| 193 | |
| 194 | projection.reset(model) |
| 195 | // Translate so the center of the car is the origin |
| 196 | .translateCenter(model, 0, 0, 0); |
| 197 | |
| 198 | for (Coord c : projection) { |
| 199 | // rotate3d(c, a, b, g); |
| 200 | colors[c.index] = colorFor(c); |
| 201 | } |
| 202 | |
| 203 | first_run = false; |
| 204 | } |
| 205 | |
| 206 | |
| 207 | // Utility! |
| 208 | boolean first_run = true; |
| 209 | private void log(String s) { |
| 210 | if (first_run) { |
| 211 | println(s); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | |
| 216 | } |
| 217 | |
| 218 | void rotate3d(Coord c, float a /* roll */, float b /* pitch */, float g /* yaw */) { |
| 219 | float cosa = cos(a); |
| 220 | float cosb = cos(b); |
| 221 | float cosg = cos(g); |
| 222 | float sina = sin(a); |
| 223 | float sinb = sin(b); |
| 224 | float sing = sin(g); |
| 225 | |
| 226 | float a1 = cosa*cosb; |
| 227 | float a2 = cosa*sinb*sing - sina*cosg; |
| 228 | float a3 = cosa*sinb*cosg + sina*sing; |
| 229 | float b1 = sina*cosb; |
| 230 | float b2 = sina*sinb*sing + cosa*cosg; |
| 231 | float b3 = sina*sinb*cosg - cosa*sing; |
| 232 | float c1 = -sinb; |
| 233 | float c2 = cosb*sing; |
| 234 | float c3 = cosb*cosg; |
| 235 | |
| 236 | float[] cArray = { c.x, c.y, c.z }; |
| 237 | c.x = dotProduct(new float[] {a1, a2, a3}, cArray); |
| 238 | c.y = dotProduct(new float[] {b1, b2, b3}, cArray); |
| 239 | c.z = dotProduct(new float[] {c1, c2, c3}, cArray); |
| 240 | } |
| 241 | |
| 242 | float dotProduct(float[] a, float[] b) { |
| 243 | float ret = 0; |
| 244 | for (int i = 0 ; i < a.length; ++i) { |
| 245 | ret += a[i] * b[i]; |
| 246 | } |
| 247 | return ret; |
| 248 | } |
| 249 | |
| 250 | color specialBlend(color c1, color c2, color c3) { |
| 251 | float h1 = hue(c1); |
| 252 | float h2 = hue(c2); |
| 253 | float h3 = hue(c3); |
| 254 | |
| 255 | // force h1 < h2 < h3 |
| 256 | while (h2 < h1) { |
| 257 | h2 += 360; |
| 258 | } |
| 259 | while (h3 < h2) { |
| 260 | h3 += 360; |
| 261 | } |
| 262 | |
| 263 | float s1 = saturation(c1); |
| 264 | float s2 = saturation(c2); |
| 265 | float s3 = saturation(c3); |
| 266 | |
| 267 | float b1 = brightness(c1); |
| 268 | float b2 = brightness(c2); |
| 269 | float b3 = brightness(c3); |
| 270 | float relative_b1 = b1 / (b1 + b2 + b3); |
| 271 | float relative_b2 = b2 / (b1 + b2 + b3); |
| 272 | float relative_b3 = b3 / (b1 + b2 + b3); |
| 273 | |
| 274 | return lx.hsb( |
| 275 | (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360, |
| 276 | s1 * relative_b1 + s2 * relative_b2 + s3 * relative_b3, |
| 277 | max(max(b1, b2), b3) |
| 278 | ); |
| 279 | } |
| 280 | |