Skip to content

Instantly share code, notes, and snippets.

@Andrew-Reid
Last active January 6, 2020 08:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Andrew-Reid/51d2665a976266abbaa62eae1ba80cdc to your computer and use it in GitHub Desktop.
Save Andrew-Reid/51d2665a976266abbaa62eae1ba80cdc to your computer and use it in GitHub Desktop.
Chevrons and Arcs 2

This block uses d3.js v4 with a custom module.

A very basic modification of d3.arc to produce an arc with chevrons, d3.chevronArc. Unlike the first attempt at this, tail ends of arcs are indented.

This block serves primarily as a proof of concept - and not as a demo of a decent module/plugin. It is based entirely off of the d3-shape module's (https://github.com/d3/d3-shape) arc function with few changes and a lot of code removed. In creating it, many or all of the checks for different cases or exceptions were removed.

Six methods are available for chevronArc(), all are required,these are:

  • innerRadius()
  • outerRadius()
  • startAngle() - in radians
  • endAngle() - in radians

All these behave the same as with d3.arc(). The two new methods are:

  • startIndent() - the indent in pixels for the tail
  • endIndent() - the indent (outdent?) in pixels for the head.
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-path')) :
typeof define === 'function' && define.amd ? define(['exports', 'd3-path'], factory) :
(factory((global.d3 = global.d3 || {}),global.d3));
}(this, (function (exports,d3Path) { 'use strict';
var constant$1 = function(x) {
return function constant() {
return x;
};
};
function chevronArcInnerRadius(d) {
return d.innerRadius;
}
function chevronArcOuterRadius(d) {
return d.outerRadius;
}
function chevronArcStartAngle(d) {
return d.startAngle;
}
function chevronArcEndAngle(d) {
return d.endAngle;
}
function chevronEndIndent(d) {
return d.endIndent;
}
function chevronStartIndent(d) {
return d.startIndent;
}
var chevronArc = function() {
var innerRadius = chevronArcInnerRadius,
outerRadius = chevronArcOuterRadius,
startAngle = chevronArcStartAngle,
endAngle = chevronArcEndAngle,
endIndent = chevronEndIndent,
startIndent = chevronStartIndent,
context = null;
function chevronArc() {
var buffer,
r,
r0 = +innerRadius.apply(this, arguments),
r1 = +outerRadius.apply(this, arguments),
a0 = startAngle.apply(this, arguments) - Math.PI/2,
a1 = endAngle.apply(this, arguments) - Math.PI/2,
e1 = +endIndent.apply(this, arguments),
e0 = +startIndent.apply(this, arguments),
da = Math.abs(a1 - a0),
cw = a1 > a0;
if (!context) context = buffer = d3Path.path();
// Ensure that the outer radius is always larger than the inner radius.
if (r1 < r0) r = r1, r1 = r0, r0 = r;
// Or is it a circular or annular sector?
else {
///////// get main coordinates:
var tailOuter = {};
tailOuter.x = Math.cos(a0) * r1; // a0 = starting angle
tailOuter.y = Math.sin(a0) * r1; // r1 = outer radius
var headOuter = {};
headOuter.x = r1 * Math.cos(a1);
headOuter.y = r1 * Math.sin(a1);
var headInner = {};
headInner.x = r0 * Math.cos(a1);
headInner.y = r0 * Math.sin(a1);
var tailInner = {};
tailInner.x = Math.cos(a0) * r0; // a0 = starting angle
tailInner.y = Math.sin(a0) * r0; // r0 = inside radius
var rDifference = e1;
//var rDifference = (r1 + r0)/4
var headMidpoint = {};
headMidpoint.x = (headInner.x + headOuter.x)/2;
headMidpoint.y = (headInner.y + headOuter.y)/2;
var tailMidpoint = {};
tailMidpoint.x = (tailInner.x + tailOuter.x)/2;
tailMidpoint.y = (tailInner.y + tailOuter.y)/2;
// get triangular points:
// head point:
var m0 = (headInner.y - headOuter.y) / (headInner.x - headOuter.x);
m0 = 1/m0;
var k0 = e1 / Math.sqrt( 1 + ( m0 * m0 ));
var headPoint = {};
if (Math.abs(m0) > 1000 * Math.PI) {
if (cw) {
headPoint.x = headMidpoint.x;
if( headInner.x < 0 ) {
headPoint.y = headMidpoint.y - e1;
}
else {
headPoint.y = headMidpoint.y + e1;
}
}
else {
headPoint.x = headMidpoint.x;
if( headInner.x < 0 ) {
headPoint.y = headMidpoint.y + e1;
}
else {
headPoint.y = headMidpoint.y - e1;
}
}
}
else {
var headAngle = a1;
while (headAngle < 0) { headAngle += Math.PI * 2; }
if ( headAngle % (Math.PI * 2) < Math.PI ) {
if (cw) {
headPoint.x = headMidpoint.x - k0;
headPoint.y = headMidpoint.y + (m0 * k0);
}
else {
headPoint.x = headMidpoint.x + k0;
headPoint.y = headMidpoint.y - (m0 * k0);
}
}
else {
if (cw) {
headPoint.x = headMidpoint.x + k0;
headPoint.y = headMidpoint.y - (m0 * k0);
}
else {
headPoint.x = headMidpoint.x - k0;
headPoint.y = headMidpoint.y + (m0 * k0);
}
}
}
// tail indent:
var m1 = (tailInner.y - tailOuter.y) / (tailInner.x - tailOuter.x);
m1 = 1/m1;
var k1 = e0 / Math.sqrt( 1 + ( m1 * m1 ));
var tailPoint = {};
if (Math.abs(m1) > 1000 * Math.PI) {
if (cw) {
tailPoint.x = tailMidpoint.x;
if( tailInner.x < 0 ) {
tailPoint.y = tailMidpoint.y - e0;
}
else {
tailPoint.y = tailMidpoint.y + e0;
}
}
else {
tailPoint.x = tailMidpoint.x;
if( tailOuter.x < 0 ) {
tailPoint.y = tailMidpoint.y + e0;
}
else {
tailPoint.y = tailMidpoint.y - e0;
}
}
}
else {
if (a0 > 0 && a0 < Math.PI) {
if (cw) {
tailPoint.x = tailMidpoint.x - k1;
tailPoint.y = tailMidpoint.y + (k1 * m1) ;
}
else {
tailPoint.x = tailMidpoint.x + k1;
tailPoint.y = tailMidpoint.y - (k1 * m1) ;
}
}
else {
if (cw) {
tailPoint.x = tailMidpoint.x + k1;
tailPoint.y = tailMidpoint.y - (k1 * m1) ;
}
else {
tailPoint.x = tailMidpoint.x - k1;
tailPoint.y = tailMidpoint.y + (k1 * m1) ;
}
}
}
/////////
// 1. start at outer curve tail
// 2. curve to outer curve head
// 3. line to head point
// 4. line to inner curve head
// 5. curve to inner curve tail
// 6. line to tail indent.
// 1, 2
context.moveTo(tailOuter.x, tailOuter.y), context.arc(0, 0, r1, a0, a1, !cw);
// 3
context.lineTo(headPoint.x, headPoint.y);
// 4, 5
context.arc(0, 0, r0, a1, a0, cw);
// 6
context.lineTo(tailPoint.x, tailPoint.y);
}
context.closePath();
if (buffer) return context = null, buffer + "" || null;
}
chevronArc.centroid = function() {
var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - Math.PI / 2;
e1 = +endAngle.apply(this,arguments);
return [Math.cos(a) * r, Math.sin(a) * r];
};
chevronArc.innerRadius = function(_) {
return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$1(+_), chevronArc) : innerRadius;
};
chevronArc.outerRadius = function(_) {
return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$1(+_), chevronArc) : outerRadius;
};
chevronArc.startAngle = function(_) {
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$1(+_), chevronArc) : startAngle;
};
chevronArc.endAngle = function(_) {
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$1(+_), chevronArc) : endAngle;
};
chevronArc.endIndent = function (_) {
return arguments.length ? (endIndent = typeof _ === "function" ? _ : constant$1(+_), chevronArc) : endIndent;
};
chevronArc.startIndent = function (_) {
return arguments.length ? (startIndent = typeof _ === "function" ? _ : constant$1(+_), chevronArc) : startIndent;
};
chevronArc.context = function(_) {
return arguments.length ? ((context = _ == null ? null : _), chevronArc) : context;
};
return chevronArc;
};
exports.chevronArc = chevronArc;
Object.defineProperty(exports, '__esModule', { value: true });
})));
<html>
<head>
</head>
<body>
<div id="svg"> </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.4.0/d3.js"></script>
<script src="chevronArcv2.js"></script>
<script>
var svg = d3.select("#svg").append("svg").attr("width",500).attr("height",500);
var width = 500;
var height = 450;
margin = {bottom: 1, top:1,left:1,right:1};
padding = 2;
marginBottom = 0;
var arcs = [];
var arcs = [
{start: 1 / 180 * Math.PI, end: 119 / 180 * Math.PI,fill:"#43a2ca",inner:150,outer:200},
{start: 121 / 180 * Math.PI, end: 239 / 180 * Math.PI,fill:"#43a2ca",inner:150,outer:200},
{start: 241 / 180 * Math.PI, end: 359 / 180 * Math.PI,fill:"#43a2ca",inner:150,outer:200},
{start: 119 / 180 * Math.PI, end: 1 / 180 * Math.PI,fill:"#0868ac",inner:205,outer:215},
{start: 239 / 180 * Math.PI, end: 121 / 180 * Math.PI,fill:"#0868ac",inner:205,outer:215},
{start: 359 / 180 * Math.PI, end: 241 / 180 * Math.PI,fill:"#0868ac",inner:205,outer:215},
{start: 199 / 180 * Math.PI, end: -159 / 180 * Math.PI,fill:"#e0f3db",inner:100,outer:145},
{start: 2 / 180 * Math.PI, end: 359 / 180 * Math.PI,fill:"#a8ddb5",inner:45,outer:95}
];
var g = svg.append("g").attr("transform", "translate(" + 500 / 2 + "," + 500 / 2 + ")");
var arc = d3.chevronArc()
.innerRadius(function(d) { return d.inner; })
.outerRadius(function(d) { return d.outer; })
.startAngle(function(d) { return d.start; })
.endAngle(function(d) { return d.end; })
.startIndent(12)
.endIndent(12);
;
g.selectAll(".arcs").data(arcs).enter().append('path')
.attr("fill", function(d) { return d.fill; })
.attr('d', function(d) {
return arc(d);
});
var circle = g.append("circle").attr("r",40).attr("fill","lightblue");
</script>
</body>
</html>
@vishalkdotcom
Copy link

Can you please suggest, How to import it in TypeScript?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment