~ubuntu-branches/ubuntu/precise/arduino/precise

« back to all changes in this revision

Viewing changes to src/processing/core/PGraphics2D.java

  • Committer: Bazaar Package Importer
  • Author(s): Scott Howard
  • Date: 2010-04-13 22:32:24 UTC
  • Revision ID: james.westby@ubuntu.com-20100413223224-jduxnd0xxnkkda02
Tags: upstream-0018+dfsg
ImportĀ upstreamĀ versionĀ 0018+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
 
2
 
 
3
/*
 
4
  Part of the Processing project - http://processing.org
 
5
 
 
6
  Copyright (c) 2006-08 Ben Fry and Casey Reas
 
7
 
 
8
  This library is free software; you can redistribute it and/or
 
9
  modify it under the terms of the GNU Lesser General Public
 
10
  License as published by the Free Software Foundation; either
 
11
  version 2.1 of the License, or (at your option) any later version.
 
12
 
 
13
  This library is distributed in the hope that it will be useful,
 
14
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
  Lesser General Public License for more details.
 
17
 
 
18
  You should have received a copy of the GNU Lesser General
 
19
  Public License along with this library; if not, write to the
 
20
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 
21
  Boston, MA  02111-1307  USA
 
22
*/
 
23
 
 
24
package processing.core;
 
25
 
 
26
import java.awt.Toolkit;
 
27
import java.awt.image.DirectColorModel;
 
28
import java.awt.image.MemoryImageSource;
 
29
import java.util.Arrays;
 
30
 
 
31
 
 
32
/**
 
33
 * Subclass of PGraphics that handles fast 2D rendering using a
 
34
 * MemoryImageSource. The renderer found in this class is not as accurate as
 
35
 * PGraphicsJava2D, but offers certain speed tradeoffs, particular when
 
36
 * messing with the pixels array, or displaying image or video data.
 
37
 */
 
38
public class PGraphics2D extends PGraphics {
 
39
 
 
40
  PMatrix2D ctm = new PMatrix2D();
 
41
 
 
42
  //PPolygon polygon;     // general polygon to use for shape
 
43
  PPolygon fpolygon;    // used to fill polys for tri or quad strips
 
44
  PPolygon spolygon;    // stroke/line polygon
 
45
  float svertices[][];  // temp vertices used for stroking end of poly
 
46
 
 
47
  PPolygon tpolygon;
 
48
  int[] vertexOrder;
 
49
 
 
50
  PLine line;
 
51
 
 
52
  float[][] matrixStack = new float[MATRIX_STACK_DEPTH][6];
 
53
  int matrixStackDepth;
 
54
 
 
55
  DirectColorModel cm;
 
56
  MemoryImageSource mis;
 
57
 
 
58
 
 
59
  //////////////////////////////////////////////////////////////
 
60
 
 
61
 
 
62
  public PGraphics2D() { }
 
63
 
 
64
 
 
65
  //public void setParent(PApplet parent)
 
66
 
 
67
 
 
68
  //public void setPrimary(boolean primary)
 
69
 
 
70
 
 
71
  //public void setPath(String path)
 
72
 
 
73
 
 
74
  //public void setSize(int iwidth, int iheight)
 
75
 
 
76
 
 
77
  protected void allocate() {
 
78
    pixelCount = width * height;
 
79
    pixels = new int[pixelCount];
 
80
 
 
81
    if (primarySurface) {
 
82
      cm = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff);;
 
83
      mis = new MemoryImageSource(width, height, pixels, 0, width);
 
84
      mis.setFullBufferUpdates(true);
 
85
      mis.setAnimated(true);
 
86
      image = Toolkit.getDefaultToolkit().createImage(mis);
 
87
    }
 
88
  }
 
89
 
 
90
 
 
91
  //public void dispose()
 
92
 
 
93
 
 
94
 
 
95
  //////////////////////////////////////////////////////////////
 
96
 
 
97
 
 
98
  public boolean canDraw() {
 
99
    return true;
 
100
  }
 
101
 
 
102
 
 
103
  public void beginDraw() {
 
104
    // need to call defaults(), but can only be done when it's ok to draw
 
105
    // (i.e. for OpenGL, no drawing can be done outside beginDraw/endDraw).
 
106
    if (!settingsInited) {
 
107
      defaultSettings();
 
108
 
 
109
//      polygon  = new PPolygon(this);
 
110
      fpolygon = new PPolygon(this);
 
111
      spolygon = new PPolygon(this);
 
112
      spolygon.vertexCount = 4;
 
113
      svertices = new float[2][];
 
114
    }
 
115
 
 
116
    resetMatrix(); // reset model matrix
 
117
 
 
118
    // reset vertices
 
119
    vertexCount = 0;
 
120
  }
 
121
 
 
122
 
 
123
  public void endDraw() {
 
124
    if (mis != null) {
 
125
      mis.newPixels(pixels, cm, 0, width);
 
126
    }
 
127
    // mark pixels as having been updated, so that they'll work properly
 
128
    // when this PGraphics is drawn using image().
 
129
    updatePixels();
 
130
  }
 
131
 
 
132
 
 
133
  // public void flush()
 
134
 
 
135
 
 
136
 
 
137
  //////////////////////////////////////////////////////////////
 
138
 
 
139
 
 
140
  //protected void checkSettings()
 
141
 
 
142
 
 
143
  //protected void defaultSettings()
 
144
 
 
145
 
 
146
  //protected void reapplySettings()
 
147
 
 
148
 
 
149
 
 
150
  //////////////////////////////////////////////////////////////
 
151
 
 
152
 
 
153
  //public void hint(int which)
 
154
 
 
155
 
 
156
 
 
157
  //////////////////////////////////////////////////////////////
 
158
 
 
159
 
 
160
  //public void beginShape()
 
161
 
 
162
 
 
163
  public void beginShape(int kind) {
 
164
    shape = kind;
 
165
    vertexCount = 0;
 
166
    curveVertexCount = 0;
 
167
 
 
168
//    polygon.reset(0);
 
169
    fpolygon.reset(4);
 
170
    spolygon.reset(4);
 
171
 
 
172
    textureImage = null;
 
173
//    polygon.interpUV = false;
 
174
  }
 
175
 
 
176
 
 
177
  //public void edge(boolean e)
 
178
 
 
179
 
 
180
  //public void normal(float nx, float ny, float nz)
 
181
 
 
182
 
 
183
  //public void textureMode(int mode)
 
184
 
 
185
 
 
186
  //public void texture(PImage image)
 
187
 
 
188
 
 
189
  /*
 
190
  public void vertex(float x, float y) {
 
191
    if (shape == POINTS) {
 
192
      point(x, y);
 
193
    } else {
 
194
      super.vertex(x, y);
 
195
    }
 
196
  }
 
197
  */
 
198
 
 
199
 
 
200
  public void vertex(float x, float y, float z) {
 
201
    showDepthWarningXYZ("vertex");
 
202
  }
 
203
 
 
204
 
 
205
  //public void vertex(float x, float y, float u, float v)
 
206
 
 
207
 
 
208
  public void vertex(float x, float y, float z, float u, float v) {
 
209
    showDepthWarningXYZ("vertex");
 
210
  }
 
211
 
 
212
 
 
213
  //protected void vertexTexture(float u, float v);
 
214
 
 
215
 
 
216
  public void breakShape() {
 
217
    showWarning("This renderer cannot handle concave shapes " +
 
218
                "or shapes with holes.");
 
219
  }
 
220
 
 
221
 
 
222
  //public void endShape()
 
223
 
 
224
 
 
225
  public void endShape(int mode) {
 
226
    if (ctm.isIdentity()) {
 
227
      for (int i = 0; i < vertexCount; i++) {
 
228
        vertices[i][TX] = vertices[i][X];
 
229
        vertices[i][TY] = vertices[i][Y];
 
230
      }
 
231
    } else {
 
232
      for (int i = 0; i < vertexCount; i++) {
 
233
        vertices[i][TX] = ctm.multX(vertices[i][X], vertices[i][Y]);
 
234
        vertices[i][TY] = ctm.multY(vertices[i][X], vertices[i][Y]);
 
235
      }
 
236
    }
 
237
 
 
238
    // ------------------------------------------------------------------
 
239
    // TEXTURES
 
240
 
 
241
    fpolygon.texture(textureImage);
 
242
 
 
243
    // ------------------------------------------------------------------
 
244
    // COLORS
 
245
    // calculate RGB for each vertex
 
246
 
 
247
    spolygon.interpARGB = true; //strokeChanged; //false;
 
248
    fpolygon.interpARGB = true; //fillChanged; //false;
 
249
 
 
250
    // all the values for r, g, b have been set with calls to vertex()
 
251
    // (no need to re-calculate anything here)
 
252
 
 
253
    // ------------------------------------------------------------------
 
254
    // RENDER SHAPES
 
255
 
 
256
    int increment;
 
257
 
 
258
    switch (shape) {
 
259
    case POINTS:
 
260
      // stroke cannot change inside beginShape(POINTS);
 
261
      if (stroke) {
 
262
        if ((ctm.m00 == ctm.m11) && (strokeWeight == 1)) {
 
263
          for (int i = 0; i < vertexCount; i++) {
 
264
            thin_point(vertices[i][TX], vertices[i][TY], strokeColor);
 
265
          }
 
266
        } else {
 
267
          for (int i = 0; i < vertexCount; i++) {
 
268
            float[] v = vertices[i];
 
269
            thick_point(v[TX], v[TY], v[TZ],  v[SR], v[SG], v[SB], v[SA]);
 
270
          }
 
271
        }
 
272
      }
 
273
      break;
 
274
 
 
275
    case LINES:
 
276
      if (stroke) {
 
277
        // increment by two for individual lines
 
278
        increment = (shape == LINES) ? 2 : 1;
 
279
        draw_lines(vertices, vertexCount-1, 1, increment, 0);
 
280
      }
 
281
      break;
 
282
 
 
283
    case TRIANGLE_FAN:
 
284
      // do fill and stroke separately because otherwise
 
285
      // the lines will be stroked more than necessary
 
286
      if (fill || textureImage != null) {
 
287
        fpolygon.vertexCount = 3;
 
288
 
 
289
        for (int i = 1; i < vertexCount-1; i++) {
 
290
//          System.out.println(i + " of " + vertexCount);
 
291
 
 
292
          fpolygon.vertices[2][R] = vertices[0][R];
 
293
          fpolygon.vertices[2][G] = vertices[0][G];
 
294
          fpolygon.vertices[2][B] = vertices[0][B];
 
295
          fpolygon.vertices[2][A] = vertices[0][A];
 
296
 
 
297
          fpolygon.vertices[2][TX] = vertices[0][TX];
 
298
          fpolygon.vertices[2][TY] = vertices[0][TY];
 
299
 
 
300
          if (textureImage != null) {
 
301
            fpolygon.vertices[2][U] = vertices[0][U];
 
302
            fpolygon.vertices[2][V] = vertices[0][V];
 
303
          }
 
304
//          System.out.println(fpolygon.vertices[2][TX] + " " + fpolygon.vertices[2][TY]);
 
305
 
 
306
          for (int j = 0; j < 2; j++) {
 
307
            fpolygon.vertices[j][R] = vertices[i+j][R];
 
308
            fpolygon.vertices[j][G] = vertices[i+j][G];
 
309
            fpolygon.vertices[j][B] = vertices[i+j][B];
 
310
            fpolygon.vertices[j][A] = vertices[i+j][A];
 
311
 
 
312
            fpolygon.vertices[j][TX] = vertices[i+j][TX];
 
313
            fpolygon.vertices[j][TY] = vertices[i+j][TY];
 
314
 
 
315
//            System.out.println(fpolygon.vertices[j][TX] + " " + fpolygon.vertices[j][TY]);
 
316
 
 
317
            if (textureImage != null) {
 
318
              fpolygon.vertices[j][U] = vertices[i+j][U];
 
319
              fpolygon.vertices[j][V] = vertices[i+j][V];
 
320
            }
 
321
          }
 
322
//          System.out.println();
 
323
          fpolygon.render();
 
324
        }
 
325
      }
 
326
      if (stroke) {
 
327
        // draw internal lines
 
328
        for (int i = 1; i < vertexCount; i++) {
 
329
          draw_line(vertices[0], vertices[i]);
 
330
        }
 
331
        // draw a ring around the outside
 
332
        for (int i = 1; i < vertexCount-1; i++) {
 
333
          draw_line(vertices[i], vertices[i+1]);
 
334
        }
 
335
        // close the shape
 
336
        draw_line(vertices[vertexCount-1], vertices[1]);
 
337
      }
 
338
      break;
 
339
 
 
340
    case TRIANGLES:
 
341
    case TRIANGLE_STRIP:
 
342
      increment = (shape == TRIANGLES) ? 3 : 1;
 
343
      // do fill and stroke separately because otherwise
 
344
      // the lines will be stroked more than necessary
 
345
      if (fill || textureImage != null) {
 
346
        fpolygon.vertexCount = 3;
 
347
        for (int i = 0; i < vertexCount-2; i += increment) {
 
348
          for (int j = 0; j < 3; j++) {
 
349
            fpolygon.vertices[j][R] = vertices[i+j][R];
 
350
            fpolygon.vertices[j][G] = vertices[i+j][G];
 
351
            fpolygon.vertices[j][B] = vertices[i+j][B];
 
352
            fpolygon.vertices[j][A] = vertices[i+j][A];
 
353
 
 
354
            fpolygon.vertices[j][TX] = vertices[i+j][TX];
 
355
            fpolygon.vertices[j][TY] = vertices[i+j][TY];
 
356
            fpolygon.vertices[j][TZ] = vertices[i+j][TZ];
 
357
 
 
358
            if (textureImage != null) {
 
359
              fpolygon.vertices[j][U] = vertices[i+j][U];
 
360
              fpolygon.vertices[j][V] = vertices[i+j][V];
 
361
            }
 
362
          }
 
363
          fpolygon.render();
 
364
        }
 
365
      }
 
366
      if (stroke) {
 
367
        // first draw all vertices as a line strip
 
368
        if (shape == TRIANGLE_STRIP) {
 
369
          draw_lines(vertices, vertexCount-1, 1, 1, 0);
 
370
        } else {
 
371
          draw_lines(vertices, vertexCount-1, 1, 1, 3);
 
372
        }
 
373
        // then draw from vertex (n) to (n+2)
 
374
        // incrementing n using the same as above
 
375
        draw_lines(vertices, vertexCount-2, 2, increment, 0);
 
376
        // changed this to vertexCount-2, because it seemed
 
377
        // to be adding an extra (nonexistant) line
 
378
      }
 
379
      break;
 
380
 
 
381
    case QUADS:
 
382
      if (fill || textureImage != null) {
 
383
        fpolygon.vertexCount = 4;
 
384
        for (int i = 0; i < vertexCount-3; i += 4) {
 
385
          for (int j = 0; j < 4; j++) {
 
386
            int jj = i+j;
 
387
            fpolygon.vertices[j][R] = vertices[jj][R];
 
388
            fpolygon.vertices[j][G] = vertices[jj][G];
 
389
            fpolygon.vertices[j][B] = vertices[jj][B];
 
390
            fpolygon.vertices[j][A] = vertices[jj][A];
 
391
 
 
392
            fpolygon.vertices[j][TX] = vertices[jj][TX];
 
393
            fpolygon.vertices[j][TY] = vertices[jj][TY];
 
394
            fpolygon.vertices[j][TZ] = vertices[jj][TZ];
 
395
 
 
396
            if (textureImage != null) {
 
397
              fpolygon.vertices[j][U] = vertices[jj][U];
 
398
              fpolygon.vertices[j][V] = vertices[jj][V];
 
399
            }
 
400
          }
 
401
          fpolygon.render();
 
402
        }
 
403
      }
 
404
      if (stroke) {
 
405
        for (int i = 0; i < vertexCount-3; i += 4) {
 
406
          draw_line(vertices[i+0], vertices[i+1]);
 
407
          draw_line(vertices[i+1], vertices[i+2]);
 
408
          draw_line(vertices[i+2], vertices[i+3]);
 
409
          draw_line(vertices[i+3], vertices[i+0]);
 
410
        }
 
411
      }
 
412
      break;
 
413
 
 
414
    case QUAD_STRIP:
 
415
      if (fill || textureImage != null) {
 
416
        fpolygon.vertexCount = 4;
 
417
        for (int i = 0; i < vertexCount-3; i += 2) {
 
418
          for (int j = 0; j < 4; j++) {
 
419
            int jj = i+j;
 
420
            if (j == 2) jj = i+3;  // swap 2nd and 3rd vertex
 
421
            if (j == 3) jj = i+2;
 
422
 
 
423
            fpolygon.vertices[j][R] = vertices[jj][R];
 
424
            fpolygon.vertices[j][G] = vertices[jj][G];
 
425
            fpolygon.vertices[j][B] = vertices[jj][B];
 
426
            fpolygon.vertices[j][A] = vertices[jj][A];
 
427
 
 
428
            fpolygon.vertices[j][TX] = vertices[jj][TX];
 
429
            fpolygon.vertices[j][TY] = vertices[jj][TY];
 
430
            fpolygon.vertices[j][TZ] = vertices[jj][TZ];
 
431
 
 
432
            if (textureImage != null) {
 
433
              fpolygon.vertices[j][U] = vertices[jj][U];
 
434
              fpolygon.vertices[j][V] = vertices[jj][V];
 
435
            }
 
436
          }
 
437
          fpolygon.render();
 
438
        }
 
439
      }
 
440
      if (stroke) {
 
441
        draw_lines(vertices, vertexCount-1, 1, 2, 0);  // inner lines
 
442
        draw_lines(vertices, vertexCount-2, 2, 1, 0);  // outer lines
 
443
      }
 
444
      break;
 
445
 
 
446
    case POLYGON:
 
447
      if (isConvex()) {
 
448
        if (fill || textureImage != null) {
 
449
          //System.out.println("convex");
 
450
          fpolygon.renderPolygon(vertices, vertexCount);
 
451
          //if (stroke) polygon.unexpand();
 
452
        }
 
453
 
 
454
        if (stroke) {
 
455
          draw_lines(vertices, vertexCount-1, 1, 1, 0);
 
456
          if (mode == CLOSE) {
 
457
            // draw the last line connecting back to the first point in poly
 
458
            //svertices[0] = vertices[vertexCount-1];
 
459
            //svertices[1] = vertices[0];
 
460
            //draw_lines(svertices, 1, 1, 1, 0);
 
461
            draw_line(vertices[vertexCount-1], vertices[0]);
 
462
          }
 
463
        }
 
464
      } else {  // not convex
 
465
        //System.out.println("concave");
 
466
        if (fill || textureImage != null) {
 
467
          // the triangulator produces polygons that don't align
 
468
          // when smoothing is enabled. but if there is a stroke around
 
469
          // the polygon, then smoothing can be temporarily disabled.
 
470
          boolean smoov = smooth;
 
471
          //if (stroke && !hints[DISABLE_SMOOTH_HACK]) smooth = false;
 
472
          if (stroke) smooth = false;
 
473
          concaveRender();
 
474
          //if (stroke && !hints[DISABLE_SMOOTH_HACK]) smooth = smoov;
 
475
          if (stroke) smooth = smoov;
 
476
        }
 
477
 
 
478
        if (stroke) {
 
479
          draw_lines(vertices, vertexCount-1, 1, 1, 0);
 
480
          if (mode == CLOSE) {
 
481
            // draw the last line connecting back
 
482
            // to the first point in poly
 
483
//            svertices[0] = vertices[vertexCount-1];
 
484
//            svertices[1] = vertices[0];
 
485
//            draw_lines(svertices, 1, 1, 1, 0);
 
486
            draw_line(vertices[vertexCount-1], vertices[0]);
 
487
          }
 
488
        }
 
489
      }
 
490
      break;
 
491
    }
 
492
 
 
493
    // to signify no shape being drawn
 
494
    shape = 0;
 
495
  }
 
496
 
 
497
 
 
498
 
 
499
  //////////////////////////////////////////////////////////////
 
500
 
 
501
  // CONCAVE/CONVEX POLYGONS
 
502
 
 
503
 
 
504
  private boolean isConvex() {
 
505
    //float v[][] = polygon.vertices;
 
506
    //int n = polygon.vertexCount;
 
507
    //int j,k;
 
508
    //float tol = 0.001f;
 
509
 
 
510
    if (vertexCount < 3) {
 
511
      // ERROR: this is a line or a point, render as convex
 
512
      return true;
 
513
    }
 
514
 
 
515
    int flag = 0;
 
516
    // iterate along border doing dot product.
 
517
    // if the sign of the result changes, then is concave
 
518
    for (int i = 0; i < vertexCount; i++) {
 
519
      float[] vi = vertices[i];
 
520
      float[] vj = vertices[(i + 1) % vertexCount];
 
521
      float[] vk = vertices[(i + 2) % vertexCount];
 
522
      float calc = ((vj[TX] - vi[TX]) * (vk[TY] - vj[TY]) -
 
523
                    (vj[TY] - vi[TY]) * (vk[TX] - vj[TX]));
 
524
      if (calc < 0) {
 
525
        flag |= 1;
 
526
      } else if (calc > 0) {
 
527
        flag |= 2;
 
528
      }
 
529
      if (flag == 3) {
 
530
        return false;  // CONCAVE
 
531
      }
 
532
    }
 
533
    if (flag != 0) {
 
534
      return true;    // CONVEX
 
535
    } else {
 
536
      // ERROR: colinear points, self intersection
 
537
      // treat as CONVEX
 
538
      return true;
 
539
    }
 
540
  }
 
541
 
 
542
 
 
543
  /**
 
544
   * Triangulate the current polygon.
 
545
   * <BR> <BR>
 
546
   * Simple ear clipping polygon triangulation adapted from code by
 
547
   * John W. Ratcliff (jratcliff at verant.com). Presumably
 
548
   * <A HREF="http://www.flipcode.org/cgi-bin/fcarticles.cgi?show=63943">this</A>
 
549
   * bit of code from the web.
 
550
   */
 
551
  protected void concaveRender() {
 
552
    if (vertexOrder == null || vertexOrder.length != vertices.length) {
 
553
      vertexOrder = new int[vertices.length];
 
554
//      int[] temp = new int[vertices.length];
 
555
//      // since vertex_start may not be zero, might need to keep old stuff around
 
556
//      PApplet.arrayCopy(vertexOrder, temp, vertexCount);
 
557
//      vertexOrder = temp;
 
558
    }
 
559
 
 
560
    if (tpolygon == null) {
 
561
      tpolygon = new PPolygon(this);
 
562
    }
 
563
    tpolygon.reset(3);
 
564
 
 
565
    // first we check if the polygon goes clockwise or counterclockwise
 
566
    float area = 0;
 
567
    for (int p = vertexCount - 1, q = 0; q < vertexCount; p = q++) {
 
568
      area += (vertices[q][X] * vertices[p][Y] -
 
569
               vertices[p][X] * vertices[q][Y]);
 
570
    }
 
571
    // ain't nuthin there
 
572
    if (area == 0) return;
 
573
 
 
574
    // don't allow polygons to come back and meet themselves,
 
575
    // otherwise it will anger the triangulator
 
576
    // http://dev.processing.org/bugs/show_bug.cgi?id=97
 
577
    float vfirst[] = vertices[0];
 
578
    float vlast[] = vertices[vertexCount-1];
 
579
    if ((Math.abs(vfirst[X] - vlast[X]) < EPSILON) &&
 
580
        (Math.abs(vfirst[Y] - vlast[Y]) < EPSILON) &&
 
581
        (Math.abs(vfirst[Z] - vlast[Z]) < EPSILON)) {
 
582
      vertexCount--;
 
583
    }
 
584
 
 
585
    // then sort the vertices so they are always in a counterclockwise order
 
586
    for (int i = 0; i < vertexCount; i++) {
 
587
      vertexOrder[i] = (area > 0) ? i : (vertexCount-1 - i);
 
588
    }
 
589
 
 
590
    // remove vc-2 Vertices, creating 1 triangle every time
 
591
    int vc = vertexCount;  // vc will be decremented while working
 
592
    int count = 2*vc;  // complex polygon detection
 
593
 
 
594
    for (int m = 0, v = vc - 1; vc > 2; ) {
 
595
      boolean snip = true;
 
596
 
 
597
      // if we start over again, is a complex polygon
 
598
      if (0 >= (count--)) {
 
599
        break; // triangulation failed
 
600
      }
 
601
 
 
602
      // get 3 consecutive vertices <u,v,w>
 
603
      int u = v ; if (vc <= u) u = 0;    // previous
 
604
      v = u + 1; if (vc <= v) v = 0;     // current
 
605
      int w = v + 1; if (vc <= w) w = 0; // next
 
606
 
 
607
      // Upgrade values to doubles, and multiply by 10 so that we can have
 
608
      // some better accuracy as we tessellate. This seems to have negligible
 
609
      // speed differences on Windows and Intel Macs, but causes a 50% speed
 
610
      // drop for PPC Macs with the bug's example code that draws ~200 points
 
611
      // in a concave polygon. Apple has abandoned PPC so we may as well too.
 
612
      // http://dev.processing.org/bugs/show_bug.cgi?id=774
 
613
 
 
614
      // triangle A B C
 
615
      double Ax = -10 * vertices[vertexOrder[u]][X];
 
616
      double Ay =  10 * vertices[vertexOrder[u]][Y];
 
617
      double Bx = -10 * vertices[vertexOrder[v]][X];
 
618
      double By =  10 * vertices[vertexOrder[v]][Y];
 
619
      double Cx = -10 * vertices[vertexOrder[w]][X];
 
620
      double Cy =  10 * vertices[vertexOrder[w]][Y];
 
621
 
 
622
      // first we check if <u,v,w> continues going ccw
 
623
      if (EPSILON > (((Bx-Ax) * (Cy-Ay)) - ((By-Ay) * (Cx-Ax)))) {
 
624
        continue;
 
625
      }
 
626
 
 
627
      for (int p = 0; p < vc; p++) {
 
628
        if ((p == u) || (p == v) || (p == w)) {
 
629
          continue;
 
630
        }
 
631
 
 
632
        double Px = -10 * vertices[vertexOrder[p]][X];
 
633
        double Py =  10 * vertices[vertexOrder[p]][Y];
 
634
 
 
635
        double ax  = Cx - Bx;  double ay  = Cy - By;
 
636
        double bx  = Ax - Cx;  double by  = Ay - Cy;
 
637
        double cx  = Bx - Ax;  double cy  = By - Ay;
 
638
        double apx = Px - Ax;  double apy = Py - Ay;
 
639
        double bpx = Px - Bx;  double bpy = Py - By;
 
640
        double cpx = Px - Cx;  double cpy = Py - Cy;
 
641
 
 
642
        double aCROSSbp = ax * bpy - ay * bpx;
 
643
        double cCROSSap = cx * apy - cy * apx;
 
644
        double bCROSScp = bx * cpy - by * cpx;
 
645
 
 
646
        if ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)) {
 
647
          snip = false;
 
648
        }
 
649
      }
 
650
 
 
651
      if (snip) {
 
652
        tpolygon.renderTriangle(vertices[vertexOrder[u]],
 
653
                                vertices[vertexOrder[v]],
 
654
                                vertices[vertexOrder[w]]);
 
655
        m++;
 
656
 
 
657
        // remove v from remaining polygon
 
658
        for (int s = v, t = v + 1; t < vc; s++, t++) {
 
659
          vertexOrder[s] = vertexOrder[t];
 
660
        }
 
661
        vc--;
 
662
 
 
663
        // reset error detection counter
 
664
        count = 2 * vc;
 
665
      }
 
666
    }
 
667
  }
 
668
 
 
669
 
 
670
  /*
 
671
  // triangulate the current polygon
 
672
  private void concaveRender() {
 
673
    float polyVertices[][] = polygon.vertices;
 
674
 
 
675
    if (tpolygon == null) {
 
676
      // allocate on first use, rather than slowing
 
677
      // the startup of the class.
 
678
      tpolygon = new PPolygon(this);
 
679
      tpolygon_vertex_order = new int[TPOLYGON_MAX_VERTICES];
 
680
    }
 
681
    tpolygon.reset(3);
 
682
 
 
683
    // copy render parameters
 
684
 
 
685
    if (textureImage != null) {
 
686
      tpolygon.texture(textureImage); //polygon.timage);
 
687
    }
 
688
 
 
689
    tpolygon.interpX = polygon.interpX;
 
690
    tpolygon.interpUV = polygon.interpUV;
 
691
    tpolygon.interpARGB = polygon.interpARGB;
 
692
 
 
693
    // simple ear clipping polygon triangulation
 
694
    // addapted from code by john w. ratcliff (jratcliff@verant.com)
 
695
 
 
696
    // 1 - first we check if the polygon goes CW or CCW
 
697
    // CW-CCW ordering adapted from code by
 
698
    //        Joseph O'Rourke orourke@cs.smith.edu
 
699
    // 1A - we start by finding the lowest-right most vertex
 
700
 
 
701
    boolean ccw = false; // clockwise
 
702
 
 
703
    int n = polygon.vertexCount;
 
704
    int mm; // postion for LR vertex
 
705
    //float min[] = new float[2];
 
706
    float minX = polyVertices[0][TX];
 
707
    float minY = polyVertices[0][TY];
 
708
    mm = 0;
 
709
 
 
710
    for(int i = 0; i < n; i++ ) {
 
711
      if ((polyVertices[i][TY] < minY) ||
 
712
          ((polyVertices[i][TY] == minY) && (polyVertices[i][TX] > minX) )
 
713
      ) {
 
714
        mm = i;
 
715
        minX = polyVertices[mm][TX];
 
716
        minY = polyVertices[mm][TY];
 
717
      }
 
718
    }
 
719
 
 
720
    // 1B - now we compute the cross product of the edges of this vertex
 
721
    float cp;
 
722
    int mm1;
 
723
 
 
724
    // just for renaming
 
725
    float a[] = new float[2];
 
726
    float b[] = new float[2];
 
727
    float c[] = new float[2];
 
728
 
 
729
    mm1 = (mm + (n-1)) % n;
 
730
 
 
731
    // assign a[0] to point to poly[m1][0] etc.
 
732
    for(int i = 0; i < 2; i++ ) {
 
733
      a[i] = polyVertices[mm1][i];
 
734
      b[i] = polyVertices[mm][i];
 
735
      c[i] = polyVertices[(mm+1)%n][i];
 
736
    }
 
737
 
 
738
    cp = a[0] * b[1] - a[1] * b[0] +
 
739
    a[1] * c[0] - a[0] * c[1] +
 
740
    b[0] * c[1] - c[0] * b[1];
 
741
 
 
742
    if ( cp > 0 )
 
743
      ccw = true;   // CCW
 
744
    else
 
745
      ccw = false;  // CW
 
746
 
 
747
    // 1C - then we sort the vertices so they
 
748
    // are always in a counterclockwise order
 
749
    //int j = 0;
 
750
    if (!ccw) {
 
751
      // keep the same order
 
752
      for (int i = 0; i < n; i++) {
 
753
        tpolygon_vertex_order[i] = i;
 
754
      }
 
755
 
 
756
    } else {
 
757
      // invert the order
 
758
      for (int i = 0; i < n; i++) {
 
759
        tpolygon_vertex_order[i] = (n - 1) - i;
 
760
      }
 
761
    }
 
762
 
 
763
    // 2 - begin triangulation
 
764
    // resulting triangles are stored in the triangle array
 
765
    // remove vc-2 Vertices, creating 1 triangle every time
 
766
    int vc = n;
 
767
    int count = 2*vc;  // complex polygon detection
 
768
 
 
769
    for (int m = 0, v = vc - 1; vc > 2; ) {
 
770
      boolean snip = true;
 
771
 
 
772
      // if we start over again, is a complex polygon
 
773
      if (0 >= (count--)) {
 
774
        break; // triangulation failed
 
775
      }
 
776
 
 
777
      // get 3 consecutive vertices <u,v,w>
 
778
      int u = v ; if (vc <= u) u = 0; // previous
 
779
      v = u+1; if (vc <= v) v = 0;    // current
 
780
      int w = v+1; if (vc <= w) w = 0;  // next
 
781
 
 
782
      // triangle A B C
 
783
      float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
 
784
 
 
785
      Ax =  -polyVertices[tpolygon_vertex_order[u]][TX];
 
786
      Ay =   polyVertices[tpolygon_vertex_order[u]][TY];
 
787
      Bx =  -polyVertices[tpolygon_vertex_order[v]][TX];
 
788
      By =   polyVertices[tpolygon_vertex_order[v]][TY];
 
789
      Cx =  -polyVertices[tpolygon_vertex_order[w]][TX];
 
790
      Cy =   polyVertices[tpolygon_vertex_order[w]][TY];
 
791
 
 
792
      if ( EPSILON > (((Bx-Ax) * (Cy-Ay)) - ((By-Ay) * (Cx-Ax)))) {
 
793
        continue;
 
794
      }
 
795
 
 
796
      for (int p = 0; p < vc; p++) {
 
797
 
 
798
        // this part is a bit osbscure, basically what it does
 
799
        // is test if this tree vertices are and ear or not, looking for
 
800
        // intersections with the remaining vertices using a cross product
 
801
        float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
 
802
        float cCROSSap, bCROSScp, aCROSSbp;
 
803
 
 
804
        if( (p == u) || (p == v) || (p == w) ) {
 
805
          continue;
 
806
        }
 
807
 
 
808
        Px = -polyVertices[tpolygon_vertex_order[p]][TX];
 
809
        Py =  polyVertices[tpolygon_vertex_order[p]][TY];
 
810
 
 
811
        ax = Cx - Bx; ay = Cy - By;
 
812
        bx = Ax - Cx; by = Ay - Cy;
 
813
        cx = Bx - Ax; cy = By - Ay;
 
814
        apx= Px - Ax; apy= Py - Ay;
 
815
        bpx= Px - Bx; bpy= Py - By;
 
816
        cpx= Px - Cx; cpy= Py - Cy;
 
817
 
 
818
        aCROSSbp = ax * bpy - ay * bpx;
 
819
        cCROSSap = cx * apy - cy * apx;
 
820
        bCROSScp = bx * cpy - by * cpx;
 
821
 
 
822
        if ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)) {
 
823
          snip = false;
 
824
        }
 
825
      }
 
826
 
 
827
      if (snip) {
 
828
        // yes, the trio is an ear, render it and cut it
 
829
 
 
830
        int triangle_vertices[] = new int[3];
 
831
        int s,t;
 
832
 
 
833
        // true names of the vertices
 
834
        triangle_vertices[0] = tpolygon_vertex_order[u];
 
835
        triangle_vertices[1] = tpolygon_vertex_order[v];
 
836
        triangle_vertices[2] = tpolygon_vertex_order[w];
 
837
 
 
838
        // create triangle
 
839
        //render_triangle(triangle_vertices);
 
840
        //private final void render_triangle(int[] triangle_vertices) {
 
841
        // copy all fields of the triangle vertices
 
842
        for (int i = 0; i < 3; i++) {
 
843
          float[] src = polygon.vertices[triangle_vertices[i]];
 
844
          float[] dest = tpolygon.vertices[i];
 
845
          for (int k = 0; k < VERTEX_FIELD_COUNT; k++) {
 
846
            dest[k] = src[k];
 
847
          }
 
848
        }
 
849
        // render triangle
 
850
        tpolygon.render();
 
851
        //}
 
852
 
 
853
        m++;
 
854
 
 
855
        // remove v from remaining polygon
 
856
        for( s = v, t = v + 1; t < vc; s++, t++) {
 
857
          tpolygon_vertex_order[s] = tpolygon_vertex_order[t];
 
858
        }
 
859
 
 
860
        vc--;
 
861
 
 
862
        // resest error detection counter
 
863
        count = 2 * vc;
 
864
      }
 
865
    }
 
866
  }
 
867
  */
 
868
 
 
869
 
 
870
 
 
871
  //////////////////////////////////////////////////////////////
 
872
 
 
873
  // BEZIER VERTICES
 
874
 
 
875
 
 
876
  //public void bezierVertex(float x2, float y2,
 
877
  //                         float x3, float y3,
 
878
  //                         float x4, float y4)
 
879
 
 
880
 
 
881
  //public void bezierVertex(float x2, float y2, float z2,
 
882
  //                         float x3, float y3, float z3,
 
883
  //                         float x4, float y4, float z4)
 
884
 
 
885
 
 
886
 
 
887
  //////////////////////////////////////////////////////////////
 
888
 
 
889
  // CURVE VERTICES
 
890
 
 
891
 
 
892
  //public void curveVertex(float x, float y)
 
893
 
 
894
 
 
895
  //public void curveVertex(float x, float y, float z)
 
896
 
 
897
 
 
898
 
 
899
  //////////////////////////////////////////////////////////////
 
900
 
 
901
  // FLUSH
 
902
 
 
903
 
 
904
  //public void flush()
 
905
 
 
906
 
 
907
 
 
908
  //////////////////////////////////////////////////////////////
 
909
 
 
910
  // PRIMITIVES
 
911
 
 
912
 
 
913
  //public void point(float x, float y)
 
914
 
 
915
 
 
916
  public void point(float x, float y, float z) {
 
917
    showDepthWarningXYZ("point");
 
918
  }
 
919
 
 
920
 
 
921
  //public void line(float x1, float y1, float x2, float y2)
 
922
 
 
923
 
 
924
  //public void line(float x1, float y1, float z1,
 
925
  //                 float x2, float y2, float z2)
 
926
 
 
927
 
 
928
  //public void triangle(float x1, float y1,
 
929
  //                     float x2, float y2,
 
930
  //                     float x3, float y3)
 
931
 
 
932
 
 
933
  //public void quad(float x1, float y1, float x2, float y2,
 
934
  //                 float x3, float y3, float x4, float y4)
 
935
 
 
936
 
 
937
 
 
938
  //////////////////////////////////////////////////////////////
 
939
 
 
940
  // RECT
 
941
 
 
942
 
 
943
  protected void rectImpl(float x1f, float y1f, float x2f, float y2f) {
 
944
    if (smooth || strokeAlpha || ctm.isWarped()) {
 
945
      // screw the efficiency, send this off to beginShape().
 
946
      super.rectImpl(x1f, y1f, x2f, y2f);
 
947
 
 
948
    } else {
 
949
      int x1 = (int) (x1f + ctm.m02);
 
950
      int y1 = (int) (y1f + ctm.m12);
 
951
      int x2 = (int) (x2f + ctm.m02);
 
952
      int y2 = (int) (y2f + ctm.m12);
 
953
 
 
954
      if (fill) {
 
955
        simple_rect_fill(x1, y1, x2, y2);
 
956
      }
 
957
 
 
958
      if (stroke) {
 
959
        if (strokeWeight == 1) {
 
960
          thin_flat_line(x1, y1, x2, y1);
 
961
          thin_flat_line(x2, y1, x2, y2);
 
962
          thin_flat_line(x2, y2, x1, y2);
 
963
          thin_flat_line(x1, y2, x1, y1);
 
964
 
 
965
        } else {
 
966
          thick_flat_line(x1, y1, strokeR, strokeG, strokeB, strokeA,
 
967
                          x2, y1, strokeR, strokeG, strokeB, strokeA);
 
968
          thick_flat_line(x2, y1, strokeR, strokeG, strokeB, strokeA,
 
969
                          x2, y2, strokeR, strokeG, strokeB, strokeA);
 
970
          thick_flat_line(x2, y2, strokeR, strokeG, strokeB, strokeA,
 
971
                          x1, y2, strokeR, strokeG, strokeB, strokeA);
 
972
          thick_flat_line(x1, y2, strokeR, strokeG, strokeB, strokeA,
 
973
                          x1, y1, strokeR, strokeG, strokeB, strokeA);
 
974
        }
 
975
      }
 
976
    }
 
977
  }
 
978
 
 
979
 
 
980
  /**
 
981
   * Draw a rectangle that hasn't been warped by the CTM (though it may be
 
982
   * translated). Just fill a bunch of pixels, or blend them if there's alpha.
 
983
   */
 
984
  private void simple_rect_fill(int x1, int y1, int x2, int y2) {
 
985
    if (y2 < y1) {
 
986
      int temp = y1; y1 = y2; y2 = temp;
 
987
    }
 
988
    if (x2 < x1) {
 
989
      int temp = x1; x1 = x2; x2 = temp;
 
990
    }
 
991
    // check to see if completely off-screen (e.g. if the left edge of the
 
992
    // rectangle is bigger than the width, and so on.)
 
993
    if ((x1 > width1) || (x2 < 0) ||
 
994
        (y1 > height1) || (y2 < 0)) return;
 
995
 
 
996
    // these only affect the fill, not the stroke
 
997
    // (otherwise strange boogers at edges b/c frame changes shape)
 
998
    if (x1 < 0) x1 = 0;
 
999
    if (x2 > width) x2 = width;
 
1000
    if (y1 < 0) y1 = 0;
 
1001
    if (y2 > height) y2 = height;
 
1002
 
 
1003
    int ww = x2 - x1;
 
1004
 
 
1005
    if (fillAlpha) {
 
1006
      for (int y = y1; y < y2; y++) {
 
1007
        int index = y*width + x1;
 
1008
        for (int x = 0; x < ww; x++) {
 
1009
          pixels[index] = blend_fill(pixels[index]);
 
1010
          index++;
 
1011
        }
 
1012
      }
 
1013
 
 
1014
    } else {
 
1015
      // on avg. 20-25% faster fill routine using System.arraycopy() [toxi 031223]
 
1016
      // removed temporary row[] array for (hopefully) better performance [fry 081117]
 
1017
      int hh = y2 - y1;
 
1018
      //    int[] row = new int[ww];
 
1019
      //    for (int i = 0; i < ww; i++) row[i] = fillColor;
 
1020
      int index = y1 * width + x1;
 
1021
      int rowIndex = index;
 
1022
      for (int i = 0; i < ww; i++) {
 
1023
        pixels[index + i] = fillColor;
 
1024
      }
 
1025
      for (int y = 0; y < hh; y++) {
 
1026
        //      System.arraycopy(row, 0, pixels, idx, ww);
 
1027
        System.arraycopy(pixels, rowIndex, pixels, index, ww);
 
1028
        index += width;
 
1029
      }
 
1030
      //    row = null;
 
1031
    }
 
1032
  }
 
1033
 
 
1034
 
 
1035
 
 
1036
  //////////////////////////////////////////////////////////////
 
1037
 
 
1038
  // ELLIPSE AND ARC
 
1039
 
 
1040
 
 
1041
  protected void ellipseImpl(float x, float y, float w, float h) {
 
1042
    if (smooth || (strokeWeight != 1) ||
 
1043
        fillAlpha || strokeAlpha || ctm.isWarped()) {
 
1044
      // identical to PGraphics version, but uses POLYGON
 
1045
      // for the fill instead of a TRIANGLE_FAN
 
1046
      float radiusH = w / 2;
 
1047
      float radiusV = h / 2;
 
1048
 
 
1049
      float centerX = x + radiusH;
 
1050
      float centerY = y + radiusV;
 
1051
 
 
1052
      float sx1 = screenX(x, y);
 
1053
      float sy1 = screenY(x, y);
 
1054
      float sx2 = screenX(x+w, y+h);
 
1055
      float sy2 = screenY(x+w, y+h);
 
1056
      int accuracy = (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / 8);
 
1057
      if (accuracy < 4) return;  // don't bother?
 
1058
      //System.out.println("diameter " + w + " " + h + " -> " + accuracy);
 
1059
 
 
1060
      float inc = (float)SINCOS_LENGTH / accuracy;
 
1061
 
 
1062
      float val = 0;
 
1063
 
 
1064
      if (fill) {
 
1065
        boolean savedStroke = stroke;
 
1066
        stroke = false;
 
1067
 
 
1068
        beginShape();
 
1069
        for (int i = 0; i < accuracy; i++) {
 
1070
          vertex(centerX + cosLUT[(int) val] * radiusH,
 
1071
                 centerY + sinLUT[(int) val] * radiusV);
 
1072
          val += inc;
 
1073
        }
 
1074
        endShape(CLOSE);
 
1075
 
 
1076
        stroke = savedStroke;
 
1077
      }
 
1078
 
 
1079
      if (stroke) {
 
1080
        boolean savedFill = fill;
 
1081
        fill = false;
 
1082
 
 
1083
        val = 0;
 
1084
        beginShape();
 
1085
        for (int i = 0; i < accuracy; i++) {
 
1086
          vertex(centerX + cosLUT[(int) val] * radiusH,
 
1087
                 centerY + sinLUT[(int) val] * radiusV);
 
1088
          val += inc;
 
1089
        }
 
1090
        endShape(CLOSE);
 
1091
 
 
1092
        fill = savedFill;
 
1093
      }
 
1094
    } else {
 
1095
      float hradius = w / 2f;
 
1096
      float vradius = h / 2f;
 
1097
 
 
1098
      int centerX = (int) (x + hradius + ctm.m02);
 
1099
      int centerY = (int) (y + vradius + ctm.m12);
 
1100
 
 
1101
      int hradiusi = (int) hradius;
 
1102
      int vradiusi = (int) vradius;
 
1103
 
 
1104
      if (hradiusi == vradiusi) {
 
1105
        if (fill) flat_circle_fill(centerX, centerY, hradiusi);
 
1106
        if (stroke) flat_circle_stroke(centerX, centerY, hradiusi);
 
1107
 
 
1108
      } else {
 
1109
        if (fill) flat_ellipse_internal(centerX, centerY, hradiusi, vradiusi, true);
 
1110
        if (stroke) flat_ellipse_internal(centerX, centerY, hradiusi, vradiusi, false);
 
1111
      }
 
1112
    }
 
1113
  }
 
1114
 
 
1115
 
 
1116
  /**
 
1117
   * Draw the outline around a flat circle using a bresenham-style
 
1118
   * algorithm. Adapted from drawCircle function in "Computer Graphics
 
1119
   * for Java Programmers" by Leen Ammeraal, p. 110.
 
1120
   * <P/>
 
1121
   * This function is included because the quality is so much better,
 
1122
   * and the drawing significantly faster than with adaptive ellipses
 
1123
   * drawn using the sine/cosine tables.
 
1124
   * <P/>
 
1125
   * Circle quadrants break down like so:
 
1126
   * <PRE>
 
1127
   *              |
 
1128
   *        \ NNW | NNE /
 
1129
   *          \   |   /
 
1130
   *       WNW  \ | /  ENE
 
1131
   *     -------------------
 
1132
   *       WSW  / | \  ESE
 
1133
   *          /   |   \
 
1134
   *        / SSW | SSE \
 
1135
   *              |
 
1136
   * </PRE>
 
1137
   * @param xc x center
 
1138
   * @param yc y center
 
1139
   * @param r radius
 
1140
   */
 
1141
  private void flat_circle_stroke(int xC, int yC, int r) {
 
1142
    int x = 0, y = r, u = 1, v = 2 * r - 1, E = 0;
 
1143
    while (x < y) {
 
1144
      thin_point(xC + x, yC + y, strokeColor); // NNE
 
1145
      thin_point(xC + y, yC - x, strokeColor); // ESE
 
1146
      thin_point(xC - x, yC - y, strokeColor); // SSW
 
1147
      thin_point(xC - y, yC + x, strokeColor); // WNW
 
1148
 
 
1149
      x++; E += u; u += 2;
 
1150
      if (v < 2 * E) {
 
1151
        y--; E -= v; v -= 2;
 
1152
      }
 
1153
      if (x > y) break;
 
1154
 
 
1155
      thin_point(xC + y, yC + x, strokeColor); // ENE
 
1156
      thin_point(xC + x, yC - y, strokeColor); // SSE
 
1157
      thin_point(xC - y, yC - x, strokeColor); // WSW
 
1158
      thin_point(xC - x, yC + y, strokeColor); // NNW
 
1159
    }
 
1160
  }
 
1161
 
 
1162
 
 
1163
  /**
 
1164
   * Heavily adapted version of the above algorithm that handles
 
1165
   * filling the ellipse. Works by drawing from the center and
 
1166
   * outwards to the points themselves. Has to be done this way
 
1167
   * because the values for the points are changed halfway through
 
1168
   * the function, making it impossible to just store a series of
 
1169
   * left and right edges to be drawn more quickly.
 
1170
   *
 
1171
   * @param xc x center
 
1172
   * @param yc y center
 
1173
   * @param r radius
 
1174
   */
 
1175
  private void flat_circle_fill(int xc, int yc, int r) {
 
1176
    int x = 0, y = r, u = 1, v = 2 * r - 1, E = 0;
 
1177
    while (x < y) {
 
1178
      for (int xx = xc; xx < xc + x; xx++) {  // NNE
 
1179
        thin_point(xx, yc + y, fillColor);
 
1180
      }
 
1181
      for (int xx = xc; xx < xc + y; xx++) {  // ESE
 
1182
        thin_point(xx, yc - x, fillColor);
 
1183
      }
 
1184
      for (int xx = xc - x; xx < xc; xx++) {  // SSW
 
1185
        thin_point(xx, yc - y, fillColor);
 
1186
      }
 
1187
      for (int xx = xc - y; xx < xc; xx++) {  // WNW
 
1188
        thin_point(xx, yc + x, fillColor);
 
1189
      }
 
1190
 
 
1191
      x++; E += u; u += 2;
 
1192
      if (v < 2 * E) {
 
1193
        y--; E -= v; v -= 2;
 
1194
      }
 
1195
      if (x > y) break;
 
1196
 
 
1197
      for (int xx = xc; xx < xc + y; xx++) {  // ENE
 
1198
        thin_point(xx, yc + x, fillColor);
 
1199
      }
 
1200
      for (int xx = xc; xx < xc + x; xx++) {  // SSE
 
1201
        thin_point(xx, yc - y, fillColor);
 
1202
      }
 
1203
      for (int xx = xc - y; xx < xc; xx++) {  // WSW
 
1204
        thin_point(xx, yc - x, fillColor);
 
1205
      }
 
1206
      for (int xx = xc - x; xx < xc; xx++) {  // NNW
 
1207
        thin_point(xx, yc + y, fillColor);
 
1208
      }
 
1209
    }
 
1210
  }
 
1211
 
 
1212
 
 
1213
  // unfortunately this can't handle fill and stroke simultaneously,
 
1214
  // because the fill will later replace some of the stroke points
 
1215
 
 
1216
  private final void flat_ellipse_symmetry(int centerX, int centerY,
 
1217
                                           int ellipseX, int ellipseY,
 
1218
                                           boolean filling) {
 
1219
    if (filling) {
 
1220
      for (int i = centerX - ellipseX + 1; i < centerX + ellipseX; i++) {
 
1221
        thin_point(i, centerY - ellipseY, fillColor);
 
1222
        thin_point(i, centerY + ellipseY, fillColor);
 
1223
      }
 
1224
    } else {
 
1225
      thin_point(centerX - ellipseX, centerY + ellipseY, strokeColor);
 
1226
      thin_point(centerX + ellipseX, centerY + ellipseY, strokeColor);
 
1227
      thin_point(centerX - ellipseX, centerY - ellipseY, strokeColor);
 
1228
      thin_point(centerX + ellipseX, centerY - ellipseY, strokeColor);
 
1229
    }
 
1230
  }
 
1231
 
 
1232
 
 
1233
  /**
 
1234
   * Bresenham-style ellipse drawing function, adapted from a posting to
 
1235
   * comp.graphics.algortihms.
 
1236
   *
 
1237
   * This function is included because the quality is so much better,
 
1238
   * and the drawing significantly faster than with adaptive ellipses
 
1239
   * drawn using the sine/cosine tables.
 
1240
   *
 
1241
   * @param centerX x coordinate of the center
 
1242
   * @param centerY y coordinate of the center
 
1243
   * @param a horizontal radius
 
1244
   * @param b vertical radius
 
1245
   */
 
1246
  private void flat_ellipse_internal(int centerX, int centerY,
 
1247
                                     int a, int b, boolean filling) {
 
1248
    int x, y, a2, b2, s, t;
 
1249
 
 
1250
    a2 = a*a;
 
1251
    b2 = b*b;
 
1252
    x = 0;
 
1253
    y = b;
 
1254
    s = a2*(1-2*b) + 2*b2;
 
1255
    t = b2 - 2*a2*(2*b-1);
 
1256
    flat_ellipse_symmetry(centerX, centerY, x, y, filling);
 
1257
 
 
1258
    do {
 
1259
      if (s < 0) {
 
1260
        s += 2*b2*(2*x+3);
 
1261
        t += 4*b2*(x+1);
 
1262
        x++;
 
1263
      } else if (t < 0) {
 
1264
        s += 2*b2*(2*x+3) - 4*a2*(y-1);
 
1265
        t += 4*b2*(x+1) - 2*a2*(2*y-3);
 
1266
        x++;
 
1267
        y--;
 
1268
      } else {
 
1269
        s -= 4*a2*(y-1);
 
1270
        t -= 2*a2*(2*y-3);
 
1271
        y--;
 
1272
      }
 
1273
      flat_ellipse_symmetry(centerX, centerY, x, y, filling);
 
1274
 
 
1275
    } while (y > 0);
 
1276
  }
 
1277
 
 
1278
 
 
1279
  // TODO really need a decent arc function in here..
 
1280
 
 
1281
  protected void arcImpl(float x, float y, float w, float h,
 
1282
                         float start, float stop) {
 
1283
    float hr = w / 2f;
 
1284
    float vr = h / 2f;
 
1285
 
 
1286
    float centerX = x + hr;
 
1287
    float centerY = y + vr;
 
1288
 
 
1289
    if (fill) {
 
1290
      // shut off stroke for a minute
 
1291
      boolean savedStroke = stroke;
 
1292
      stroke = false;
 
1293
 
 
1294
      int startLUT = (int) (-0.5f + (start / TWO_PI) * SINCOS_LENGTH);
 
1295
      int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
 
1296
 
 
1297
      beginShape();
 
1298
      vertex(centerX, centerY);
 
1299
      for (int i = startLUT; i < stopLUT; i++) {
 
1300
        int ii = i % SINCOS_LENGTH;
 
1301
        // modulo won't make the value positive
 
1302
        if (ii < 0) ii += SINCOS_LENGTH;
 
1303
        vertex(centerX + cosLUT[ii] * hr,
 
1304
               centerY + sinLUT[ii] * vr);
 
1305
      }
 
1306
      endShape(CLOSE);
 
1307
 
 
1308
      stroke = savedStroke;
 
1309
    }
 
1310
 
 
1311
    if (stroke) {
 
1312
      // Almost identical to above, but this uses a LINE_STRIP
 
1313
      // and doesn't include the first (center) vertex.
 
1314
 
 
1315
      boolean savedFill = fill;
 
1316
      fill = false;
 
1317
 
 
1318
      int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
 
1319
      int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
 
1320
 
 
1321
      beginShape(); //LINE_STRIP);
 
1322
      int increment = 1; // what's a good algorithm? stopLUT - startLUT;
 
1323
      for (int i = startLUT; i < stopLUT; i += increment) {
 
1324
        int ii = i % SINCOS_LENGTH;
 
1325
        if (ii < 0) ii += SINCOS_LENGTH;
 
1326
        vertex(centerX + cosLUT[ii] * hr,
 
1327
               centerY + sinLUT[ii] * vr);
 
1328
      }
 
1329
      // draw last point explicitly for accuracy
 
1330
      vertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
 
1331
             centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
 
1332
      endShape();
 
1333
 
 
1334
      fill = savedFill;
 
1335
    }
 
1336
  }
 
1337
 
 
1338
 
 
1339
 
 
1340
  //////////////////////////////////////////////////////////////
 
1341
 
 
1342
  // BOX
 
1343
 
 
1344
 
 
1345
  public void box(float size) {
 
1346
    showDepthWarning("box");
 
1347
  }
 
1348
 
 
1349
  public void box(float w, float h, float d) {
 
1350
    showDepthWarning("box");
 
1351
  }
 
1352
 
 
1353
 
 
1354
 
 
1355
  //////////////////////////////////////////////////////////////
 
1356
 
 
1357
  // SPHERE
 
1358
 
 
1359
 
 
1360
  public void sphereDetail(int res) {
 
1361
    showDepthWarning("sphereDetail");
 
1362
  }
 
1363
 
 
1364
  public void sphereDetail(int ures, int vres) {
 
1365
    showDepthWarning("sphereDetail");
 
1366
  }
 
1367
 
 
1368
  public void sphere(float r) {
 
1369
    showDepthWarning("sphere");
 
1370
  }
 
1371
 
 
1372
 
 
1373
 
 
1374
  //////////////////////////////////////////////////////////////
 
1375
 
 
1376
  // BEZIER & CURVE
 
1377
 
 
1378
 
 
1379
  public void bezier(float x1, float y1, float z1,
 
1380
                     float x2, float y2, float z2,
 
1381
                     float x3, float y3, float z3,
 
1382
                     float x4, float y4, float z4) {
 
1383
    showDepthWarningXYZ("bezier");
 
1384
  }
 
1385
 
 
1386
 
 
1387
  public void curve(float x1, float y1, float z1,
 
1388
                    float x2, float y2, float z2,
 
1389
                    float x3, float y3, float z3,
 
1390
                    float x4, float y4, float z4) {
 
1391
    showDepthWarningXYZ("curve");
 
1392
  }
 
1393
 
 
1394
 
 
1395
 
 
1396
  //////////////////////////////////////////////////////////////
 
1397
 
 
1398
  // IMAGE
 
1399
 
 
1400
 
 
1401
  protected void imageImpl(PImage image,
 
1402
                           float x1, float y1, float x2, float y2,
 
1403
                           int u1, int v1, int u2, int v2) {
 
1404
    if ((x2 - x1 == image.width) &&
 
1405
        (y2 - y1 == image.height) &&
 
1406
        !tint && !ctm.isWarped()) {
 
1407
      simple_image(image, (int) (x1 + ctm.m02), (int) (y1 + ctm.m12), u1, v1, u2, v2);
 
1408
 
 
1409
    } else {
 
1410
      super.imageImpl(image, x1, y1, x2, y2, u1, v1, u2, v2);
 
1411
    }
 
1412
  }
 
1413
 
 
1414
 
 
1415
  /**
 
1416
   * Image drawn in flat "screen space", with no scaling or warping.
 
1417
   * this is so common that a special routine is included for it,
 
1418
   * because the alternative is much slower.
 
1419
   *
 
1420
   * @param  image  image to be drawn
 
1421
   * @param  sx1    x coordinate of upper-lefthand corner in screen space
 
1422
   * @param  sy1    y coordinate of upper-lefthand corner in screen space
 
1423
   */
 
1424
  private void simple_image(PImage image, int sx1, int sy1,
 
1425
                            int ix1, int iy1, int ix2, int iy2) {
 
1426
    int sx2 = sx1 + image.width;
 
1427
    int sy2 = sy1 + image.height;
 
1428
 
 
1429
    // don't draw if completely offscreen
 
1430
    // (without this check, ArrayIndexOutOfBoundsException)
 
1431
    if ((sx1 > width1) || (sx2 < 0) ||
 
1432
        (sy1 > height1) || (sy2 < 0)) return;
 
1433
 
 
1434
    if (sx1 < 0) {  // off left edge
 
1435
      ix1 -= sx1;
 
1436
      sx1 = 0;
 
1437
    }
 
1438
    if (sy1 < 0) {  // off top edge
 
1439
      iy1 -= sy1;
 
1440
      sy1 = 0;
 
1441
    }
 
1442
    if (sx2 > width) {  // off right edge
 
1443
      ix2 -= sx2 - width;
 
1444
      sx2 = width;
 
1445
    }
 
1446
    if (sy2 > height) {  // off bottom edge
 
1447
      iy2 -= sy2 - height;
 
1448
      sy2 = height;
 
1449
    }
 
1450
 
 
1451
    int source = iy1 * image.width + ix1;
 
1452
    int target = sy1 * width;
 
1453
 
 
1454
    if (image.format == ARGB) {
 
1455
      for (int y = sy1; y < sy2; y++) {
 
1456
        int tx = 0;
 
1457
 
 
1458
        for (int x = sx1; x < sx2; x++) {
 
1459
          pixels[target + x] =
 
1460
//            _blend(pixels[target + x],
 
1461
//                   image.pixels[source + tx],
 
1462
//                   image.pixels[source + tx++] >>> 24);
 
1463
          blend_color(pixels[target + x],
 
1464
                      image.pixels[source + tx++]);
 
1465
        }
 
1466
        source += image.width;
 
1467
        target += width;
 
1468
      }
 
1469
    } else if (image.format == ALPHA) {
 
1470
      for (int y = sy1; y < sy2; y++) {
 
1471
        int tx = 0;
 
1472
 
 
1473
        for (int x = sx1; x < sx2; x++) {
 
1474
          pixels[target + x] =
 
1475
            blend_color_alpha(pixels[target + x],
 
1476
                              fillColor,
 
1477
                              image.pixels[source + tx++]);
 
1478
        }
 
1479
        source += image.width;
 
1480
        target += width;
 
1481
      }
 
1482
 
 
1483
    } else if (image.format == RGB) {
 
1484
      target += sx1;
 
1485
      int tw = sx2 - sx1;
 
1486
      for (int y = sy1; y < sy2; y++) {
 
1487
        System.arraycopy(image.pixels, source, pixels, target, tw);
 
1488
        // should set z coordinate in here
 
1489
        // or maybe not, since dims=0, meaning no relevant z
 
1490
        source += image.width;
 
1491
        target += width;
 
1492
      }
 
1493
    }
 
1494
  }
 
1495
 
 
1496
 
 
1497
  //////////////////////////////////////////////////////////////
 
1498
 
 
1499
  // TEXT/FONTS
 
1500
 
 
1501
 
 
1502
  // These will be handled entirely by PGraphics.
 
1503
 
 
1504
 
 
1505
 
 
1506
  //////////////////////////////////////////////////////////////
 
1507
 
 
1508
  // UGLY RENDERING SHITE
 
1509
 
 
1510
 
 
1511
  // expects properly clipped coords, hence does
 
1512
    // NOT check if x/y are in bounds [toxi]
 
1513
    private void thin_point_at(int x, int y, float z, int color) {
 
1514
      int index = y*width+x; // offset values are pre-calced in constructor
 
1515
      pixels[index] = color;
 
1516
    }
 
1517
 
 
1518
    // expects offset/index in pixelbuffer array instead of x/y coords
 
1519
    // used by optimized parts of thin_flat_line() [toxi]
 
1520
    private void thin_point_at_index(int offset, float z, int color) {
 
1521
      pixels[offset] = color;
 
1522
    }
 
1523
 
 
1524
 
 
1525
    private void thick_point(float x, float y, float z, // note floats
 
1526
                             float r, float g, float b, float a) {
 
1527
      spolygon.reset(4);
 
1528
      spolygon.interpARGB = false;  // no changes for vertices of a point
 
1529
 
 
1530
      float strokeWidth2 = strokeWeight/2.0f;
 
1531
 
 
1532
      float svertex[] = spolygon.vertices[0];
 
1533
      svertex[TX] = x - strokeWidth2;
 
1534
      svertex[TY] = y - strokeWidth2;
 
1535
      svertex[TZ] = z;
 
1536
 
 
1537
      svertex[R] = r;
 
1538
      svertex[G] = g;
 
1539
      svertex[B] = b;
 
1540
      svertex[A] = a;
 
1541
 
 
1542
      svertex = spolygon.vertices[1];
 
1543
      svertex[TX] = x + strokeWidth2;
 
1544
      svertex[TY] = y - strokeWidth2;
 
1545
      svertex[TZ] = z;
 
1546
 
 
1547
      svertex = spolygon.vertices[2];
 
1548
      svertex[TX] = x + strokeWidth2;
 
1549
      svertex[TY] = y + strokeWidth2;
 
1550
      svertex[TZ] = z;
 
1551
 
 
1552
      svertex = spolygon.vertices[3];
 
1553
      svertex[TX] = x - strokeWidth2;
 
1554
      svertex[TY] = y + strokeWidth2;
 
1555
      svertex[TZ] = z;
 
1556
 
 
1557
      spolygon.render();
 
1558
    }
 
1559
 
 
1560
 
 
1561
    // new bresenham clipping code, as old one was buggy [toxi]
 
1562
    private void thin_flat_line(int x1, int y1, int x2, int y2) {
 
1563
      int nx1,ny1,nx2,ny2;
 
1564
 
 
1565
      // get the "dips" for the points to clip
 
1566
      int code1 = thin_flat_line_clip_code(x1, y1);
 
1567
      int code2 = thin_flat_line_clip_code(x2, y2);
 
1568
 
 
1569
      if ((code1 & code2)!=0) {
 
1570
        return;
 
1571
      } else {
 
1572
        int dip = code1 | code2;
 
1573
        if (dip != 0) {
 
1574
          // now calculate the clipped points
 
1575
          float a1 = 0, a2 = 1, a = 0;
 
1576
          for (int i=0;i<4;i++) {
 
1577
            if (((dip>>i)%2)==1) {
 
1578
              a = thin_flat_line_slope((float)x1, (float)y1,
 
1579
                                      (float)x2, (float)y2, i+1);
 
1580
              if (((code1>>i)%2)==1) {
 
1581
                a1 = (float)Math.max(a, a1);
 
1582
              } else {
 
1583
                a2 = (float)Math.min(a, a2);
 
1584
              }
 
1585
            }
 
1586
          }
 
1587
          if (a1>a2) return;
 
1588
          else {
 
1589
            nx1=(int) (x1+a1*(x2-x1));
 
1590
            ny1=(int) (y1+a1*(y2-y1));
 
1591
            nx2=(int) (x1+a2*(x2-x1));
 
1592
            ny2=(int) (y1+a2*(y2-y1));
 
1593
          }
 
1594
          // line is fully visible/unclipped
 
1595
        } else {
 
1596
          nx1=x1; nx2=x2;
 
1597
          ny1=y1; ny2=y2;
 
1598
        }
 
1599
      }
 
1600
 
 
1601
      // new "extremely fast" line code
 
1602
      // adapted from http://www.edepot.com/linee.html
 
1603
 
 
1604
      boolean yLonger=false;
 
1605
      int shortLen=ny2-ny1;
 
1606
      int longLen=nx2-nx1;
 
1607
      if (Math.abs(shortLen)>Math.abs(longLen)) {
 
1608
        int swap=shortLen;
 
1609
        shortLen=longLen;
 
1610
        longLen=swap;
 
1611
        yLonger=true;
 
1612
      }
 
1613
      int decInc;
 
1614
      if (longLen==0) decInc=0;
 
1615
      else decInc = (shortLen << 16) / longLen;
 
1616
 
 
1617
      if (nx1==nx2) {
 
1618
        // special case: vertical line
 
1619
        if (ny1>ny2) { int ty=ny1; ny1=ny2; ny2=ty; }
 
1620
        int offset=ny1*width+nx1;
 
1621
        for(int j=ny1; j<=ny2; j++) {
 
1622
          thin_point_at_index(offset,0,strokeColor);
 
1623
          offset+=width;
 
1624
        }
 
1625
        return;
 
1626
      } else if (ny1==ny2) {
 
1627
        // special case: horizontal line
 
1628
        if (nx1>nx2) { int tx=nx1; nx1=nx2; nx2=tx; }
 
1629
        int offset=ny1*width+nx1;
 
1630
        for(int j=nx1; j<=nx2; j++) thin_point_at_index(offset++,0,strokeColor);
 
1631
        return;
 
1632
      } else if (yLonger) {
 
1633
        if (longLen>0) {
 
1634
          longLen+=ny1;
 
1635
          for (int j=0x8000+(nx1<<16);ny1<=longLen;++ny1) {
 
1636
            thin_point_at(j>>16, ny1, 0, strokeColor);
 
1637
            j+=decInc;
 
1638
          }
 
1639
          return;
 
1640
        }
 
1641
        longLen+=ny1;
 
1642
        for (int j=0x8000+(nx1<<16);ny1>=longLen;--ny1) {
 
1643
          thin_point_at(j>>16, ny1, 0, strokeColor);
 
1644
          j-=decInc;
 
1645
        }
 
1646
        return;
 
1647
      } else if (longLen>0) {
 
1648
        longLen+=nx1;
 
1649
        for (int j=0x8000+(ny1<<16);nx1<=longLen;++nx1) {
 
1650
          thin_point_at(nx1, j>>16, 0, strokeColor);
 
1651
          j+=decInc;
 
1652
        }
 
1653
        return;
 
1654
      }
 
1655
      longLen+=nx1;
 
1656
      for (int j=0x8000+(ny1<<16);nx1>=longLen;--nx1) {
 
1657
        thin_point_at(nx1, j>>16, 0, strokeColor);
 
1658
        j-=decInc;
 
1659
      }
 
1660
    }
 
1661
 
 
1662
 
 
1663
    private int thin_flat_line_clip_code(float x, float y) {
 
1664
      return ((y < 0 ? 8 : 0) | (y > height1 ? 4 : 0) |
 
1665
              (x < 0 ? 2 : 0) | (x > width1 ? 1 : 0));
 
1666
    }
 
1667
 
 
1668
 
 
1669
    private float thin_flat_line_slope(float x1, float y1,
 
1670
                                      float x2, float y2, int border) {
 
1671
      switch (border) {
 
1672
      case 4: {
 
1673
        return (-y1)/(y2-y1);
 
1674
      }
 
1675
      case 3: {
 
1676
        return (height1-y1)/(y2-y1);
 
1677
      }
 
1678
      case 2: {
 
1679
        return (-x1)/(x2-x1);
 
1680
      }
 
1681
      case 1: {
 
1682
        return (width1-x1)/(x2-x1);
 
1683
      }
 
1684
      }
 
1685
      return -1f;
 
1686
    }
 
1687
 
 
1688
 
 
1689
    private void thick_flat_line(float ox1, float oy1,
 
1690
                                 float r1, float g1, float b1, float a1,
 
1691
                                 float ox2, float oy2,
 
1692
                                 float r2, float g2, float b2, float a2) {
 
1693
      spolygon.interpARGB = (r1 != r2) || (g1 != g2) || (b1 != b2) || (a1 != a2);
 
1694
//      spolygon.interpZ = false;
 
1695
 
 
1696
      float dX = ox2-ox1 + EPSILON;
 
1697
      float dY = oy2-oy1 + EPSILON;
 
1698
      float len = (float) Math.sqrt(dX*dX + dY*dY);
 
1699
 
 
1700
      // TODO stroke width should be transformed!
 
1701
      float rh = (strokeWeight / len) / 2;
 
1702
 
 
1703
      float dx0 = rh * dY;
 
1704
      float dy0 = rh * dX;
 
1705
      float dx1 = rh * dY;
 
1706
      float dy1 = rh * dX;
 
1707
 
 
1708
      spolygon.reset(4);
 
1709
 
 
1710
      float svertex[] = spolygon.vertices[0];
 
1711
      svertex[TX] = ox1+dx0;
 
1712
      svertex[TY] = oy1-dy0;
 
1713
      svertex[R] = r1;
 
1714
      svertex[G] = g1;
 
1715
      svertex[B] = b1;
 
1716
      svertex[A] = a1;
 
1717
 
 
1718
      svertex = spolygon.vertices[1];
 
1719
      svertex[TX] = ox1-dx0;
 
1720
      svertex[TY] = oy1+dy0;
 
1721
      svertex[R] = r1;
 
1722
      svertex[G] = g1;
 
1723
      svertex[B] = b1;
 
1724
      svertex[A] = a1;
 
1725
 
 
1726
      svertex = spolygon.vertices[2];
 
1727
      svertex[TX] = ox2-dx1;
 
1728
      svertex[TY] = oy2+dy1;
 
1729
      svertex[R] = r2;
 
1730
      svertex[G] = g2;
 
1731
      svertex[B] = b2;
 
1732
      svertex[A] = a2;
 
1733
 
 
1734
      svertex = spolygon.vertices[3];
 
1735
      svertex[TX] = ox2+dx1;
 
1736
      svertex[TY] = oy2-dy1;
 
1737
      svertex[R] = r2;
 
1738
      svertex[G] = g2;
 
1739
      svertex[B] = b2;
 
1740
      svertex[A] = a2;
 
1741
 
 
1742
      spolygon.render();
 
1743
    }
 
1744
 
 
1745
 
 
1746
    private void draw_line(float[] v1, float[] v2) {
 
1747
      if (strokeWeight == 1) {
 
1748
        if (line == null) line = new PLine(this);
 
1749
 
 
1750
        line.reset();
 
1751
        line.setIntensities(v1[SR], v1[SG], v1[SB], v1[SA],
 
1752
                            v2[SR], v2[SG], v2[SB], v2[SA]);
 
1753
        line.setVertices(v1[TX], v1[TY], v1[TZ],
 
1754
                         v2[TX], v2[TY], v2[TZ]);
 
1755
        line.draw();
 
1756
 
 
1757
      } else {  // use old line code for thickness != 1
 
1758
        thick_flat_line(v1[TX], v1[TY],  v1[SR], v1[SG], v1[SB], v1[SA],
 
1759
                        v2[TX], v2[TY],  v2[SR], v2[SG], v2[SB], v2[SA]);
 
1760
      }
 
1761
    }
 
1762
 
 
1763
 
 
1764
    /**
 
1765
     * @param max is what to count to
 
1766
     * @param offset is offset to the 'next' vertex
 
1767
     * @param increment is how much to increment in the loop
 
1768
     */
 
1769
    private void draw_lines(float vertices[][], int max,
 
1770
                            int offset, int increment, int skip) {
 
1771
 
 
1772
      if (strokeWeight == 1) {
 
1773
        for (int i = 0; i < max; i += increment) {
 
1774
          if ((skip != 0) && (((i+offset) % skip) == 0)) continue;
 
1775
 
 
1776
          float a[] = vertices[i];
 
1777
          float b[] = vertices[i+offset];
 
1778
 
 
1779
          if (line == null) line = new PLine(this);
 
1780
 
 
1781
          line.reset();
 
1782
          line.setIntensities(a[SR], a[SG], a[SB], a[SA],
 
1783
                              b[SR], b[SG], b[SB], b[SA]);
 
1784
          line.setVertices(a[TX], a[TY], a[TZ],
 
1785
                           b[TX], b[TY], b[TZ]);
 
1786
          line.draw();
 
1787
        }
 
1788
 
 
1789
      } else {  // use old line code for thickness != 1
 
1790
        for (int i = 0; i < max; i += increment) {
 
1791
          if ((skip != 0) && (((i+offset) % skip) == 0)) continue;
 
1792
 
 
1793
          float v1[] = vertices[i];
 
1794
          float v2[] = vertices[i+offset];
 
1795
          thick_flat_line(v1[TX], v1[TY],  v1[SR], v1[SG], v1[SB], v1[SA],
 
1796
                          v2[TX], v2[TY],  v2[SR], v2[SG], v2[SB], v2[SA]);
 
1797
        }
 
1798
      }
 
1799
    }
 
1800
 
 
1801
 
 
1802
    private void thin_point(float fx, float fy, int color) {
 
1803
      int x = (int) (fx + 0.4999f);
 
1804
      int y = (int) (fy + 0.4999f);
 
1805
      if (x < 0 || x > width1 || y < 0 || y > height1) return;
 
1806
 
 
1807
      int index = y*width + x;
 
1808
      if ((color & 0xff000000) == 0xff000000) {  // opaque
 
1809
        pixels[index] = color;
 
1810
 
 
1811
      } else {  // transparent
 
1812
        // a1 is how much of the orig pixel
 
1813
        int a2 = (color >> 24) & 0xff;
 
1814
        int a1 = a2 ^ 0xff;
 
1815
 
 
1816
        int p2 = strokeColor;
 
1817
        int p1 = pixels[index];
 
1818
 
 
1819
        int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00;
 
1820
        int g = (a1 * ((p1 >>  8) & 0xff) + a2 * ((p2 >>  8) & 0xff)) & 0xff00;
 
1821
        int b = (a1 * ( p1        & 0xff) + a2 * ( p2        & 0xff)) >> 8;
 
1822
 
 
1823
        pixels[index] =  0xff000000 | (r << 8) | g | b;
 
1824
      }
 
1825
    }
 
1826
 
 
1827
 
 
1828
 
 
1829
    //////////////////////////////////////////////////////////////
 
1830
 
 
1831
    // MATRIX TRANSFORMATIONS
 
1832
 
 
1833
 
 
1834
    public void translate(float tx, float ty) {
 
1835
      ctm.translate(tx, ty);
 
1836
    }
 
1837
 
 
1838
 
 
1839
    public void translate(float tx, float ty, float tz) {
 
1840
      showDepthWarningXYZ("translate");
 
1841
    }
 
1842
 
 
1843
 
 
1844
    public void rotate(float angle) {
 
1845
      ctm.rotate(angle);
 
1846
//      float c = (float) Math.cos(angle);
 
1847
//      float s = (float) Math.sin(angle);
 
1848
//      applyMatrix(c, -s, 0,  s, c, 0);
 
1849
    }
 
1850
 
 
1851
 
 
1852
    public void rotateX(float angle) {
 
1853
      showDepthWarning("rotateX");
 
1854
    }
 
1855
 
 
1856
    public void rotateY(float angle) {
 
1857
      showDepthWarning("rotateY");
 
1858
    }
 
1859
 
 
1860
 
 
1861
    public void rotateZ(float angle) {
 
1862
      showDepthWarning("rotateZ");
 
1863
    }
 
1864
 
 
1865
 
 
1866
    public void rotate(float angle, float vx, float vy, float vz) {
 
1867
      showVariationWarning("rotate(angle, x, y, z)");
 
1868
    }
 
1869
 
 
1870
 
 
1871
    public void scale(float s) {
 
1872
      ctm.scale(s);
 
1873
//      applyMatrix(s, 0, 0,
 
1874
//                  0, s, 0);
 
1875
    }
 
1876
 
 
1877
 
 
1878
    public void scale(float sx, float sy) {
 
1879
      ctm.scale(sx, sy);
 
1880
//      applyMatrix(sx, 0, 0,
 
1881
//                  0, sy, 0);
 
1882
    }
 
1883
 
 
1884
 
 
1885
    public void scale(float x, float y, float z) {
 
1886
      showDepthWarningXYZ("scale");
 
1887
    }
 
1888
 
 
1889
 
 
1890
 
 
1891
    //////////////////////////////////////////////////////////////
 
1892
 
 
1893
    // TRANSFORMATION MATRIX
 
1894
 
 
1895
 
 
1896
    public void pushMatrix() {
 
1897
      if (matrixStackDepth == MATRIX_STACK_DEPTH) {
 
1898
        throw new RuntimeException(ERROR_PUSHMATRIX_OVERFLOW);
 
1899
      }
 
1900
      ctm.get(matrixStack[matrixStackDepth]);
 
1901
      matrixStackDepth++;
 
1902
    }
 
1903
 
 
1904
 
 
1905
    public void popMatrix() {
 
1906
      if (matrixStackDepth == 0) {
 
1907
        throw new RuntimeException(ERROR_PUSHMATRIX_UNDERFLOW);
 
1908
      }
 
1909
      matrixStackDepth--;
 
1910
      ctm.set(matrixStack[matrixStackDepth]);
 
1911
    }
 
1912
 
 
1913
 
 
1914
    /**
 
1915
     * Load identity as the transform/model matrix.
 
1916
     * Same as glLoadIdentity().
 
1917
     */
 
1918
    public void resetMatrix() {
 
1919
      ctm.reset();
 
1920
//      m00 = 1; m01 = 0; m02 = 0;
 
1921
//      m10 = 0; m11 = 1; m12 = 0;
 
1922
    }
 
1923
 
 
1924
 
 
1925
    /**
 
1926
     * Apply a 3x2 affine transformation matrix.
 
1927
     */
 
1928
    public void applyMatrix(float n00, float n01, float n02,
 
1929
                            float n10, float n11, float n12) {
 
1930
      ctm.apply(n00, n01, n02,
 
1931
                n10, n11, n12);
 
1932
//
 
1933
//      float r00 = m00*n00 + m01*n10;
 
1934
//      float r01 = m00*n01 + m01*n11;
 
1935
//      float r02 = m00*n02 + m01*n12 + m02;
 
1936
//
 
1937
//      float r10 = m10*n00 + m11*n10;
 
1938
//      float r11 = m10*n01 + m11*n11;
 
1939
//      float r12 = m10*n02 + m11*n12 + m12;
 
1940
//
 
1941
//      m00 = r00; m01 = r01; m02 = r02;
 
1942
//      m10 = r10; m11 = r11; m12 = r12;
 
1943
    }
 
1944
 
 
1945
 
 
1946
    public void applyMatrix(float n00, float n01, float n02, float n03,
 
1947
                            float n10, float n11, float n12, float n13,
 
1948
                            float n20, float n21, float n22, float n23,
 
1949
                            float n30, float n31, float n32, float n33) {
 
1950
      showDepthWarningXYZ("applyMatrix");
 
1951
    }
 
1952
 
 
1953
 
 
1954
    /**
 
1955
     * Loads the current matrix into m00, m01 etc (or modelview and
 
1956
     * projection when using 3D) so that the values can be read.
 
1957
     * <P/>
 
1958
     * Note that there is no "updateMatrix" because that gets too
 
1959
     * complicated (unnecessary) when considering the 3D matrices.
 
1960
     */
 
1961
//    public void loadMatrix() {
 
1962
      // no-op on base PGraphics because they're used directly
 
1963
//    }
 
1964
 
 
1965
 
 
1966
    /**
 
1967
     * Print the current model (or "transformation") matrix.
 
1968
     */
 
1969
    public void printMatrix() {
 
1970
      ctm.print();
 
1971
 
 
1972
//      loadMatrix();  // just to make sure
 
1973
//
 
1974
//      float big = Math.abs(m00);
 
1975
//      if (Math.abs(m01) > big) big = Math.abs(m01);
 
1976
//      if (Math.abs(m02) > big) big = Math.abs(m02);
 
1977
//      if (Math.abs(m10) > big) big = Math.abs(m10);
 
1978
//      if (Math.abs(m11) > big) big = Math.abs(m11);
 
1979
//      if (Math.abs(m12) > big) big = Math.abs(m12);
 
1980
//
 
1981
//      // avoid infinite loop
 
1982
//      if (Float.isNaN(big) || Float.isInfinite(big)) {
 
1983
//        big = 1000000; // set to something arbitrary
 
1984
//      }
 
1985
//
 
1986
//      int d = 1;
 
1987
//      int bigi = (int) big;
 
1988
//      while ((bigi /= 10) != 0) d++;  // cheap log()
 
1989
//
 
1990
//      System.out.println(PApplet.nfs(m00, d, 4) + " " +
 
1991
//                         PApplet.nfs(m01, d, 4) + " " +
 
1992
//                         PApplet.nfs(m02, d, 4));
 
1993
//
 
1994
//      System.out.println(PApplet.nfs(m10, d, 4) + " " +
 
1995
//                         PApplet.nfs(m11, d, 4) + " " +
 
1996
//                         PApplet.nfs(m12, d, 4));
 
1997
//
 
1998
//      System.out.println();
 
1999
    }
 
2000
 
 
2001
 
 
2002
 
 
2003
    //////////////////////////////////////////////////////////////
 
2004
 
 
2005
    // SCREEN TRANSFORMS
 
2006
 
 
2007
 
 
2008
    public float screenX(float x, float y) {
 
2009
      return ctm.m00 * x + ctm.m01 * y + ctm.m02;
 
2010
    }
 
2011
 
 
2012
 
 
2013
    public float screenY(float x, float y) {
 
2014
      return ctm.m10 * x + ctm.m11 * y + ctm.m12;
 
2015
    }
 
2016
 
 
2017
 
 
2018
 
 
2019
    //////////////////////////////////////////////////////////////
 
2020
 
 
2021
    // BACKGROUND AND FRIENDS
 
2022
 
 
2023
 
 
2024
    /**
 
2025
     * Clear the pixel buffer.
 
2026
     */
 
2027
    protected void backgroundImpl() {
 
2028
      Arrays.fill(pixels, backgroundColor);
 
2029
    }
 
2030
 
 
2031
 
 
2032
 
 
2033
    /*
 
2034
    public void ambient(int rgb) {
 
2035
      showDepthError("ambient");
 
2036
    }
 
2037
 
 
2038
    public void ambient(float gray) {
 
2039
      showDepthError("ambient");
 
2040
    }
 
2041
 
 
2042
    public void ambient(float x, float y, float z) {
 
2043
      // This doesn't take
 
2044
      if ((x != PMaterial.DEFAULT_AMBIENT) ||
 
2045
          (y != PMaterial.DEFAULT_AMBIENT) ||
 
2046
          (z != PMaterial.DEFAULT_AMBIENT)) {
 
2047
        showDepthError("ambient");
 
2048
      }
 
2049
    }
 
2050
 
 
2051
    public void specular(int rgb) {
 
2052
      showDepthError("specular");
 
2053
    }
 
2054
 
 
2055
    public void specular(float gray) {
 
2056
      showDepthError("specular");
 
2057
    }
 
2058
 
 
2059
    public void specular(float x, float y, float z) {
 
2060
      showDepthError("specular");
 
2061
    }
 
2062
 
 
2063
    public void shininess(float shine) {
 
2064
      showDepthError("shininess");
 
2065
    }
 
2066
 
 
2067
 
 
2068
    public void emissive(int rgb) {
 
2069
      showDepthError("emissive");
 
2070
    }
 
2071
 
 
2072
    public void emissive(float gray) {
 
2073
      showDepthError("emissive");
 
2074
    }
 
2075
 
 
2076
    public void emissive(float x, float y, float z ) {
 
2077
      showDepthError("emissive");
 
2078
    }
 
2079
    */
 
2080
 
 
2081
 
 
2082
 
 
2083
    //////////////////////////////////////////////////////////////
 
2084
 
 
2085
    // INTERNAL SCHIZZLE
 
2086
 
 
2087
 
 
2088
    // TODO make this more efficient, or move into PMatrix2D
 
2089
//    private boolean untransformed() {
 
2090
//      return ((ctm.m00 == 1) && (ctm.m01 == 0) && (ctm.m02 == 0) &&
 
2091
//              (ctm.m10 == 0) && (ctm.m11 == 1) && (ctm.m12 == 0));
 
2092
//    }
 
2093
//
 
2094
//
 
2095
//    // TODO make this more efficient, or move into PMatrix2D
 
2096
//    private boolean unwarped() {
 
2097
//      return ((ctm.m00 == 1) && (ctm.m01 == 0) &&
 
2098
//              (ctm.m10 == 0) && (ctm.m11 == 1));
 
2099
//    }
 
2100
 
 
2101
 
 
2102
    // only call this if there's an alpha in the fill
 
2103
    private final int blend_fill(int p1) {
 
2104
      int a2 = fillAi;
 
2105
      int a1 = a2 ^ 0xff;
 
2106
 
 
2107
      int r = (a1 * ((p1 >> 16) & 0xff)) + (a2 * fillRi) & 0xff00;
 
2108
      int g = (a1 * ((p1 >>  8) & 0xff)) + (a2 * fillGi) & 0xff00;
 
2109
      int b = (a1 * ( p1        & 0xff)) + (a2 * fillBi) & 0xff00;
 
2110
 
 
2111
      return 0xff000000 | (r << 8) | g | (b >> 8);
 
2112
    }
 
2113
 
 
2114
 
 
2115
    private final int blend_color(int p1, int p2) {
 
2116
      int a2 = (p2 >>> 24);
 
2117
 
 
2118
      if (a2 == 0xff) {
 
2119
        // full replacement
 
2120
        return p2;
 
2121
 
 
2122
      } else {
 
2123
        int a1 = a2 ^ 0xff;
 
2124
        int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00;
 
2125
        int g = (a1 * ((p1 >>  8) & 0xff) + a2 * ((p2 >>  8) & 0xff)) & 0xff00;
 
2126
        int b = (a1 * ( p1        & 0xff) + a2 * ( p2        & 0xff)) >> 8;
 
2127
 
 
2128
        return 0xff000000 | (r << 8) | g | b;
 
2129
      }
 
2130
    }
 
2131
 
 
2132
 
 
2133
    private final int blend_color_alpha(int p1, int p2, int a2) {
 
2134
      // scale alpha by alpha of incoming pixel
 
2135
      a2 = (a2 * (p2 >>> 24)) >> 8;
 
2136
 
 
2137
      int a1 = a2 ^ 0xff;
 
2138
      int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00;
 
2139
      int g = (a1 * ((p1 >>  8) & 0xff) + a2 * ((p2 >>  8) & 0xff)) & 0xff00;
 
2140
      int b = (a1 * ( p1        & 0xff) + a2 * ( p2        & 0xff)) >> 8;
 
2141
 
 
2142
      return 0xff000000 | (r << 8) | g | b;
 
2143
    }
 
2144
}