3 #define glTypeUnsignedShort 0x1403
4 #define glTypeFloat 0x1406
6 enum JoinType { miter, round, bevel };
7 enum CapType { butt, round, square };
9 define joinType = JoinType::bevel;
10 define capType = CapType::round;
12 class ButterburTest : Window
14 displayDriver = "OpenGL";
15 caption = $"Butterbur's Humble Beginnings";
16 background = formColor;
17 borderStyle = sizable;
21 clientSize = { 632, 438 };
25 BBRectangle rect { scene, lineColor = { 128, tomato }, fillColor = { 128, skyBlue }, box = { 30, 30, 340, 190 }, cap = capType, join = joinType, lineWidth = 10 };
26 BBCircle circle { scene, lineColor = { 128, green }, fillColor = { 170, tomato }, center = { 390, 280 }, cap = capType, join = joinType, radius = 100, lineWidth = 8 };
27 BBEllipse ell { scene, lineColor = { 128, yellow }, fillColor = { 100, black }, center = { 250, 310 }, k = 1.3, cap = capType, join = joinType, radius = 100, lineWidth = 4 };
31 scene, lineColor = { 128, blue }, fillColor = { 100, lime }, lineWidth = 20;
33 join = miter; //joinType;
36 // Pointf { 150, 80 },
45 scene, lineColor = red, lineWidth = 1;
57 scene, lineColor = purple, fillColor = { 100, orange }, lineWidth = 9;
62 // Pointf { 150, 80 },
71 scene, lineColor = black, lineWidth = 20;
73 nodes.copySrc = [ Pointf { 300, 300 } ];
76 void OnRedraw(Surface surface)
78 display.antiAlias = true;
83 ButterburTest bbTest {};
87 List<BBObject> objects { };
93 if(o.needUpdate) { o.update(); o.needUpdate = false; }
107 virtual void update();
108 virtual void render();
110 public property BBScene parent
115 parent.objects.TakeOut(this);
117 value.objects.Add(this);
119 get { return parent; }
123 class BBPath : BBObject
125 Array<Pointf> nodes { };
129 GLEAB fillIndices { };
130 GLEAB lineIndices { };
132 ColorAlpha lineColor;
133 ColorAlpha fillColor;
142 property ColorAlpha lineColor
144 set { lineColor = value; }
145 get { return lineColor; }
148 property ColorAlpha fillColor
150 set { fillColor = value; }
151 get { return fillColor; }
154 property float lineWidth
156 set { lineWidth = value; }
157 get { return lineWidth; }
165 uint tc = nodes.count;
169 if(lineWidth > 1 && (tc > 1 || cap != butt))
171 int rCount = (!noJoin && join == round) ? 7 : (!noJoin && join == bevel) ? 2 : 1;
172 int capCount = (cap == round) ? 7 : 1;
176 vboCount = closed ? (tc * (rCount+1)) : (2*(capCount+1) + ((tc > 2) ? (tc-2) * (rCount+1) : 0));
177 points = new Pointf[vboCount];
179 ixCount = closed ? (tc * rCount*2 + closed*2) :
180 (2*(2*capCount) + ((tc > 2) ? (tc-2) * (2*rCount) : 0));
181 ix = new uint16[ixCount];
183 for(i = 0; i < tc + (tc == 1); i++)
186 if(i == tc) { i = 0; end = true; }
190 Pointf before = i > 0 ? nodes[i-1] : (closed ? nodes[tc-1] : (tc > 1 ? nodes[1] : nodes[0]));
191 Pointf after = i < tc-1 ? nodes[i+1] : (closed ? nodes[0] : (tc > 1 ? nodes[i-1] : nodes[0]));
192 float ldx = p.x - before.x, ldy = p.y - before.y;
193 float rdx = after.x - p.x, rdy = after.y - p.y;
194 double at1 = atan2(ldy, ldx);
195 double at2 = atan2(rdy, rdx);
197 double diffAngle = at2 - at1;
198 bool simpleMean = true;
201 if(!closed && (i == 0 || i == tc-1)) isCap = true;
203 if(Abs(diffAngle) >= Pi)
209 diffAngle = diffAngle - 2*Pi;
219 double r = lineWidth;
222 double angle = (i == 0 && !end) ? at1 : at2;
226 double ang = angle - Pi/2 + Pi;
227 c = (float)(cos(ang) * r/2), s = (float)(sin(ang) * r/2);
228 points[startIX] = { p.x + c, p.y + s };
231 for(t = 0; t < capCount; t++)
233 double ang = angle + (i ? -Pi/2 : Pi/2) - t * Pi/(capCount-1);
234 c = (float)(cos(ang) * r/2), s = (float)(sin(ang) * r/2);
235 points[startIX + add + t] = { p.x + c, p.y + s };
239 double ang = angle + Pi/2 + Pi;
240 c = (float)(cos(ang) * r/2), s = (float)(sin(ang) * r/2);
241 points[startIX + 1 + capCount-1] = { p.x + c, p.y + s };
247 double r = lineWidth;
248 double angle = (i == 0 && !end) ? at1 : at2;
249 double ang = angle - Pi/2;
250 c = (float)(cos(ang) * r/2), s = (float)(sin(ang) * r/2);
251 points[startIX] = { p.x - c, p.y - s};
252 points[startIX+1] = { p.x + c, p.y + s };
257 double r = lineWidth * sqrt(2);
258 double angle = (i == 0 && !end) ? at1 : at2;
259 double ang = (i || end) ? (angle - Pi/4) + Pi : (angle + Pi/4);
260 c = (float)(cos(ang) * r/2), s = (float)(sin(ang) * r/2);
261 points[startIX] = { p.x + c, p.y + s};
263 ang = (i || end) ? (angle + Pi/4) + Pi : (angle - Pi/4);
264 c = (float)(cos(ang) * r/2), s = (float)(sin(ang) * r/2);
265 points[startIX+1] = { p.x + c, p.y + s };
272 double r = lineWidth / cos(diffAngle/2);
274 bool diffSigns = Sgn(at1) != Sgn(at2);
276 angle = (double)(at1 + at2) / 2;
277 else if(diffSigns && ldy > 0 && rdy < 0 && !ldx && !rdx)
279 else if(diffSigns && ldy < 0 && rdy > 0 && !ldx && !rdx)
282 angle = (double)(at1 + at2) / 2 + Pi;
286 c = (float)(cos(angle) * r/2), s = (float)(sin(angle) * r/2);
288 points[startIX] = { p.x - c, p.y - s };
295 for(t = 0; t < rCount; t++)
297 double ang = at2 - ((rCount-1-t) * diffAngle / (rCount-1)) + Pi/2;
298 c = (float)(cos(ang) * r/2), s = (float)(sin(ang) * r/2);
299 points[startIX+1+t] = { p.x + c, p.y + s };
303 points[startIX+1] = { p.x + c, p.y + s };
305 for(n = 0; n < (isCap ? capCount : rCount); n++)
308 ix[d++] = (uint16)(startIX+n+1);
310 startIX += (uint16)(isCap ? capCount : rCount) + 1;
323 points = nodes.array;
324 ixCount = tc + closed;
325 ix = new uint16[ixCount];
327 for(i = 0; i < tc; i++)
332 lineVBO.upload(vboCount*sizeof(Pointf), points);
333 lineIndices.upload(ixCount * sizeof(uint16), ix);
336 if(points != nodes.array)
343 ix = new uint16[tc + closed];
345 for(i = 0; i < tc; i++)
349 fillVBO.upload(tc*sizeof(Pointf), nodes.array);
350 fillIndices.upload(tc * sizeof(uint16), ix);
361 glimtkColor4f(fillColor.color.r/255.0f, fillColor.color.g/255.0f, fillColor.color.b/255.0f, fillColor.a/255.0f);
362 fillVBO.use(vertex, 2, glTypeFloat, 0, null);
363 fillIndices.draw(GLIMTKMode::triangleFan, nodes.count, glTypeUnsignedShort, null);
367 glimtkColor4f(lineColor.color.r/255.0f, lineColor.color.g/255.0f, lineColor.color.b/255.0f, lineColor.a/255.0f);
368 lineVBO.use(vertex, 2, glTypeFloat, 0, null);
370 lineIndices.draw(lineWidth > 1 ? GLIMTKMode::triangleStrip : GLIMTKMode::lineStrip, lineCount, glTypeUnsignedShort, null);
372 glimtkColor4f(1, 0, 0, 1);
373 // lineIndices.draw(GLIMTKMode::lineStrip, lineCount, glTypeUnsignedShort, null);
377 class BBRectangle : BBPath
384 set { box = value; needUpdate = true; }
391 nodes[0] = { box.left, box.top };
392 nodes[1] = { box.left, box.bottom };
393 nodes[2] = { box.right, box.bottom };
394 nodes[3] = { box.right, box.top };
400 class BBCircle : BBPath
410 property Pointf center
412 set { center = value; needUpdate = true; }
413 get { value = center; }
416 property float radius
418 set { radius = value; needUpdate = true; }
419 get { return radius; }
424 uint count = radius < 3 ? 6 : radius < 20 ? 12 : radius < 50 ? 30 : 100;
428 for(i = 0; i < count; i++)
430 double a = i * 2*Pi / count;
431 nodes[i] = { center.x + (float)cos(a) * radius, center.y + (float)sin(a) * radius };
438 class BBEllipse : BBPath
450 property Pointf center
452 set { center = value; needUpdate = true; }
453 get { value = center; }
456 property float radius
458 set { radius = value; needUpdate = true; }
459 get { return radius; }
464 uint count = radius < 3 ? 6 : radius < 20 ? 12 : radius < 50 ? 30 : 100;
468 for(i = 0; i < count; i++)
470 double a = i * 2*Pi / (count+1);
471 nodes[i] = { center.x + (float)cos(a) * radius * k, center.y + (float)sin(a) * radius };