Skip to content

Commit 5bc9a40

Browse files
committed
Canvas test enhancements.
1 parent bda70a8 commit 5bc9a40

File tree

2 files changed

+129
-44
lines changed

2 files changed

+129
-44
lines changed

MotionMark/resources/extensions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,12 @@ class Point {
298298

299299
length()
300300
{
301-
return Math.sqrt(this.x * this.x + this.y * this.y);
301+
return Math.hypot(this.x, this.y);
302302
}
303303

304304
normalize()
305305
{
306-
var l = Math.sqrt(this.x * this.x + this.y * this.y);
306+
const l = this.length();
307307
this.x /= l;
308308
this.y /= l;
309309
return this;

MotionMark/tests/dev/radial-chart/resources/radial-chart.js

Lines changed: 127 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,27 @@ class MathHelpers {
5353
{
5454
return (offset + Date.now() / (cycleLengthMs || 2000)) % 1;
5555
}
56+
57+
static cheapHash(s)
58+
{
59+
let hash = 0, i = 0, len = s.length;
60+
while ( i < len )
61+
hash = ((hash << 5) - hash + s.charCodeAt(i++)) << 0;
62+
63+
return hash + 2147483647 + 1;
64+
}
65+
66+
// JavaScripts % operator is remainder, not modulo.
67+
static modulo(dividend, divisor)
68+
{
69+
const quotient = Math.floor(dividend / divisor);
70+
return dividend - divisor * quotient;
71+
}
72+
73+
static normalizeRadians(radians)
74+
{
75+
return MathHelpers.modulo(radians, Math.PI * 2);
76+
}
5677
}
5778

5879
class ItemData {
@@ -61,8 +82,8 @@ class ItemData {
6182
this.deptNumber = deptNumber;
6283
this.label = label;
6384
this.imageURL = imageURL;
64-
65-
this.hueOffset = MathHelpers.random(0.1, 0.2);
85+
86+
this.hueOffset = MathHelpers.cheapHash(label) / 0xFFFFFFFF;
6687
this.colorLightness = MathHelpers.random(0.5, 0.7);
6788
this.colorSaturation = MathHelpers.random(0.2, 0.5);
6889
}
@@ -178,7 +199,7 @@ class RadialChart {
178199

179200
draw(ctx)
180201
{
181-
this.numSpokes = this._complexity - 1;
202+
this.numSpokes = this._complexity;
182203
this.wedgeAngleRadians = TwoPI / this.numSpokes;
183204
this.angleOffsetRadians = Math.PI / 2; // Start at the top, rather than the right.
184205

@@ -268,45 +289,94 @@ class RadialChart {
268289

269290
#drawWedgeLabels(ctx, index, instance)
270291
{
271-
const midAngleRadians = this.#wedgeStartAngle(index) + 0.5 * this.wedgeAngleRadians;
292+
const midAngleRadians = MathHelpers.normalizeRadians(this.#wedgeStartAngle(index) + 0.5 * this.wedgeAngleRadians);
272293

273-
const textInset = 15;
294+
const textInset = -15;
274295
const textCenterPoint = this.center.add(GeometryHelpers.createPointOnCircle(midAngleRadians, this.innerRadius - textInset));
275296

276297
const labelAngle = midAngleRadians + Math.PI / 2;
277298

278-
ctx.save();
279-
ctx.font = '12px sans-serif';
280-
ctx.fillStyle = 'black';
281-
282-
ctx.translate(textCenterPoint.x, textCenterPoint.y);
283-
ctx.rotate(labelAngle);
299+
{
300+
ctx.save();
301+
ctx.font = '12px "Helvetica Neue", Helvetica, sans-serif';
302+
303+
// Numbers on inner ring.
304+
ctx.translate(textCenterPoint.x, textCenterPoint.y);
305+
ctx.rotate(labelAngle);
284306

285-
const textSize = ctx.measureText(instance.deptNumber);
286-
ctx.fillText(instance.deptNumber, -textSize.width / 2, 0);
287-
ctx.restore();
307+
const textSize = ctx.measureText(instance.deptNumber);
308+
309+
ctx.strokeStyle = 'black';
310+
ctx.lineWidth = 1;
311+
ctx.strokeText(instance.deptNumber, -textSize.width / 2, 0);
312+
313+
{
314+
ctx.save();
315+
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
316+
ctx.shadowBlur = 5;
317+
ctx.fillStyle = 'white';
318+
ctx.fillText(instance.deptNumber, -textSize.width / 2, 0);
319+
ctx.restore();
320+
}
321+
322+
ctx.restore();
323+
}
324+
325+
// Labels around outside.
326+
const labelDistance = 20;
327+
const labelHorizontalOffset = 60;
328+
const outsideMidSegmentPoint = this.center.add(GeometryHelpers.createPointOnCircle(midAngleRadians, this.outerRadius + labelDistance));
329+
let outerLabelLocation = outsideMidSegmentPoint;
330+
const isRightSide = midAngleRadians < Math.PI /2 || midAngleRadians > Math.PI * 1.5;
331+
if (isRightSide)
332+
outerLabelLocation = outsideMidSegmentPoint.add(new Point(labelHorizontalOffset, 0));
333+
else
334+
outerLabelLocation = outsideMidSegmentPoint.add(new Point(-labelHorizontalOffset, 0));
335+
336+
{
337+
ctx.save();
338+
339+
ctx.translate(outerLabelLocation.x, outerLabelLocation.y);
340+
341+
ctx.font = '12px "Helvetica Neue", Helvetica, sans-serif';
342+
ctx.fillStyle = 'black';
288343

289-
const textLabelGap = 5;
290-
const outerLabelLocation = this.center.add(GeometryHelpers.createPointOnCircle(midAngleRadians, this.outerRadius + textLabelGap));
344+
let textOffset = 0;
345+
if (!isRightSide)
346+
textOffset = -ctx.measureText(instance.label).width;
347+
348+
ctx.fillText(instance.label, textOffset, 0);
349+
ctx.restore();
350+
}
291351

292-
ctx.save();
352+
const wedgeArrowEnd = this.center.add(GeometryHelpers.createPointOnCircle(midAngleRadians, this.outerRadius));
353+
const wedgeArrowEndAngle = MathHelpers.normalizeRadians(midAngleRadians + Math.PI);
354+
const arrowPath = this.#pathForArrow(outerLabelLocation, wedgeArrowEnd, wedgeArrowEndAngle);
355+
356+
// Arrow.
357+
{
358+
ctx.save();
359+
ctx.strokeStyle = 'gray';
360+
ctx.setLineDash([4, 2]);
361+
ctx.stroke(arrowPath);
362+
ctx.restore();
363+
}
293364

294-
ctx.translate(outerLabelLocation.x, outerLabelLocation.y);
295-
ctx.rotate(midAngleRadians + Math.PI / 2 - 0.5);
365+
// Arrowhead.
366+
{
367+
ctx.save();
368+
const arrowheadPath = this.#pathForArrowHead();
296369

297-
ctx.font = '12px sans-serif';
298-
ctx.fillStyle = 'black';
299-
ctx.fillText(instance.label, 0, 0);
300-
ctx.restore();
301-
302-
// const wedgeArrowEnd = this.center.add(GeometryHelpers.createPointOnCircle(midAngleRadians, this.outerRadius));
303-
// const arrowPath = this.#pathForArrow(outerLabelLocation, wedgeArrowEnd);
304-
//
305-
// ctx.save();
306-
// ctx.strokeStyle = 'gray';
307-
// ctx.setLineDash([10, 4]);
308-
// ctx.stroke(arrowPath);
309-
// ctx.restore();
370+
ctx.translate(wedgeArrowEnd.x, wedgeArrowEnd.y);
371+
const arrowheadSize = 12;
372+
ctx.scale(arrowheadSize, arrowheadSize);
373+
ctx.rotate(midAngleRadians);
374+
375+
ctx.fillStyle = 'gray';
376+
ctx.fill(arrowheadPath);
377+
378+
ctx.restore();
379+
}
310380
}
311381

312382
#drawBadge(ctx, index, instance)
@@ -325,7 +395,6 @@ class RadialChart {
325395
ctx.translate(imageCenterPoint.x, imageCenterPoint.y);
326396
ctx.rotate(imageAngle);
327397

328-
// FIXME: This shadow makes Safari very slow.
329398
ctx.shadowColor = "black";
330399
ctx.shadowBlur = 5;
331400

@@ -360,23 +429,39 @@ class RadialChart {
360429
}
361430
}
362431

363-
// Unused
364-
#pathForArrow(startPoint, endPoint)
432+
#pathForArrow(startPoint, endPoint, endAngle)
365433
{
366434
const arrowPath = new Path2D();
367435
arrowPath.moveTo(startPoint.x, startPoint.y);
368436
// Compute a bezier path that keeps the line horizontal at the start and end.
437+
438+
const distance = startPoint.subtract(endPoint).length();
369439

370440
const controlPointProportion = 0.5;
371-
372441
const controlPoint1 = startPoint.add({ x: controlPointProportion * (endPoint.x - startPoint.x), y: 0});
373-
const controlPoint2 = endPoint.subtract({ x: controlPointProportion * (endPoint.x - startPoint.x), y: 0});
374-
arrowPath.bezierCurveTo(controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
442+
443+
const controlPoint2Offset = new Point(controlPointProportion * distance * Math.cos(endAngle), controlPointProportion * distance * Math.sin(endAngle));
444+
const controlPoint2 = endPoint.subtract(controlPoint2Offset);
375445

376-
//arrowPath.lineTo(endPoint.x, endPoint.y);
377-
// Add arrowhead
446+
arrowPath.bezierCurveTo(controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
378447
return arrowPath;
379448
}
449+
450+
#pathForArrowHead()
451+
{
452+
// Arrowhead points left.
453+
const arrowHeadPath = new Path2D();
454+
const pointyness = 0.5;
455+
const breadth = 0.4;
456+
457+
arrowHeadPath.moveTo(0, 0);
458+
arrowHeadPath.quadraticCurveTo(pointyness, 0, 1, breadth);
459+
arrowHeadPath.lineTo(1, -breadth);
460+
arrowHeadPath.quadraticCurveTo(pointyness, 0, 0, 0);
461+
arrowHeadPath.closePath();
462+
463+
return arrowHeadPath;
464+
}
380465
}
381466

382467
class RadialChartStage extends Stage {
@@ -535,14 +620,14 @@ window.benchmarkClass = RadialChartBenchmark;
535620
class FakeController {
536621
constructor()
537622
{
538-
this.initialComplexity = 102;
623+
this.initialComplexity = 200;
539624
this.startTime = new Date;
540625
}
541626

542627
shouldStop()
543628
{
544629
const now = new Date();
545-
return (now - this.startTime) > 15000;
630+
return (now - this.startTime) > 500;
546631
}
547632

548633
results()

0 commit comments

Comments
 (0)