使用次要和主要刻度创建波特图d3js

时间:2015-12-23 10:07:47

标签: javascript d3.js plot charts

我正在尝试用d3js创建一个典型的波特图。使用Matlab创建的典型波特图如下所示:

Matlab bode plot

现在我已经相当了解:http://plnkr.co/edit/BpWis5uhC8KM2tRbXMk3或此帖中嵌入的代码段。

我遇到的问题是,我想要,就像在matlab波特图中一样,有主要和次要的滴答声。主要蜱是稳固的,而小蜱是点缀的。不幸的是,我似乎无法做到这一点。我已经尝试过各种解决方案:

Major and minor ticks with different style, whole page covered D3? Major and minor ticks with V3 of D3? d3.js alternative to axis.tickSubdivide? http://bl.ocks.org/vjpgo/4689130 http://bl.ocks.org/mbostock/4349486

<!DOCTYPE html>
<html lang="en">
<head>
<title>LOG</title>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//mathjs.org/js/lib/math.js"></script>
<style type="text/css">
svg {
    font: 10px sans-serif;
    shape-rendering: crispEdges;
}
  
rect {
    fill: transparent;
}
  
.axis path,
.axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}
  
.line {
    fill: none;
    stroke: steelblue;
    stroke-width: 1.5px;
    clip-path: url(#clip);
}
  
.grid .tick {
    stroke: lightgrey;
    opacity: 0.7;
}
  
.grid path {
    stroke-width: 0;
}
</style>
<script type="text/javascript">
	function linspace(a,b,n) {
		var every = (b-a)/(n-1),
			range = [];
		for (i = a; i < b; i += every)
			range.push(i);
		return range.length == n ? range : range.concat(b);
	}
	
	function logspace(a,b,n) {
		return linspace(a,b,n).map(function(x) { return Math.pow(10,x); });
	}
	
	function isInteger(value) {
		return typeof value === "number" && 
			   isFinite(value) && 
			   Math.floor(value) === value;
	};
	
	function leadlag(f) {
		w = 2*math.pi*f;
		s = math.complex(0,w);

		K  = 1;
		fz = 20;
		fp = 40;

		wz = 2*math.pi*fz;
		wp = 2*math.pi*fp;

		return math.multiply(K,math.multiply(math.divide(wp,wz),math.divide(math.add(s,wz),(math.add(s,wp)))));
	};
	
	function angle(f) {
		return math.atan2(f.im,f.re);
	};
	
	function deg2rad(deg) {
  		return deg * math.pi / 180;
	};
 
	function rad2deg(rad) {
  		return rad * 180 / math.pi;
	};
	
	function mag2db(mag) {
		return 20 * Math.log10(mag);
	}
	
	function db2mag(db) {
		return math.pow(10,db / 20);
	}
</script>
</head>
<body>
<div id="plotmagnitude"></div>
<div id="plotphase"></div>
<script type="text/javascript">
	var margin = {
        top: 20,
        right: 20,
        bottom: 35,
        left: 50
    };
	
	var width  = 450 - margin.left - margin.right;
	var height = 250 - margin.top - margin.bottom;
	
	var range = logspace(0,3,1000);

    var x = d3.scale.log()
		.domain([1, range[range.length-1].toFixed()])
		.range([0, width]);

    var y = d3.scale.linear()
		.domain([-10, 2])
		.range([height, 0]);

    var xAxis1 = d3.svg.axis()
		.scale(x)
		.orient("bottom")
		.ticks(1,"0.1s")
		.innerTickSize(-6)
		.outerTickSize(0)
		.tickPadding(7);

    var yAxis1 = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.innerTickSize(-6)
		.outerTickSize(0)
		.tickPadding(7);

    var xAxis2 = d3.svg.axis()
		.scale(x)
		.orient("top")
		.ticks(5)
		.innerTickSize(-6)
		.tickPadding(-20)
		.outerTickSize(0)
		.tickFormat("");

    var yAxis2 = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.innerTickSize(6)
		.tickPadding(-20)
		.outerTickSize(0)
		.tickFormat("");

    var xGrid = d3.svg.axis()
		.scale(x)
		.orient("bottom")
		.ticks(5)
		.tickSize(-height, -height, 0)
		.tickFormat("");

    var yGrid = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.tickSize(-width, -width, 0)
		.tickFormat("");
	
    var line = d3.svg.line()
		.x(function(d) {
			return x(d.x);
		})
		.y(function(d) {
			return y(d.y);
		})
		.interpolate("linear");
	  
    var zoom = d3.behavior.zoom()
		.x(x)
		.y(y)
		.scaleExtent([1, 1])
		.on("zoom",redraw);
      
    var plotMagnitude = d3.select("#plotmagnitude").append("svg")
		.attr("width",width + margin.left + margin.right)
		.attr("height",height + margin.top + margin.bottom)
		.append("g")
		.attr("transform","translate(" + margin.left + "," + margin.top + ")")
		.call(zoom);
	  
    // Add x grid
    plotMagnitude.append("g")
		.attr("class","x grid")
		.attr("transform","translate(0," + height + ")")
		.call(xGrid);
		

    // Add y grid
    plotMagnitude.append("g")
		.attr("class","y grid")
		.call(yGrid);

    plotMagnitude.append("g")
		.attr("class","x1 axis")
		.attr("transform","translate(0," + height + ")")
		.call(xAxis1);

    plotMagnitude.append("g")
		.attr("class","y1 axis")
		.call(yAxis1);

    /* append additional X axis */
    plotMagnitude.append("g")
		.attr("class","x2 axis")
		.attr("transform","translate(" + [0, 0] + ")")
		.call(xAxis2);

    /* append additional y axis */
    plotMagnitude.append("g")
		.attr("class","y2 axis")
		.attr("transform","translate(" + [width, 0] + ")")
		.call(yAxis2);

    // Add x axis label  
    plotMagnitude.append("text")
        .attr("transform","translate(" + (width / 2) + "," + (height + margin.bottom) + ")")
        .style("font-size","15")
        .style("text-anchor","middle")
        .text("x axis");

    // Add y axis label
    plotMagnitude.append("text")
        .attr("transform", "rotate(-90)")
        .attr("y",0 - margin.left)
        .attr("x",0 - (height / 2))
        .attr("dy", "1em")
        .style("font-size","15")
        .style("text-anchor", "middle")
        .text("y axis");

    plotMagnitude.append("defs").append("clipPath")
		.attr("id", "clip")
		.append("rect")
		.attr("width", width)
		.attr("height", height);
      
    plotMagnitude.append("rect")
		.attr("width", width)
		.attr("height", height);

    function redraw() {
		plotMagnitude.select(".x1.axis").call(xAxis1);
		plotMagnitude.select(".y1.axis").call(yAxis1);
		plotMagnitude.select(".x2.axis").call(xAxis2);
		plotMagnitude.select(".y2.axis").call(yAxis2);
		plotMagnitude.select(".x.grid").call(xGrid);
		plotMagnitude.select(".y.grid").call(yGrid);
		
		var series = [];
		var data1 = [];
		var data2 = [];
		var data3 = [];

		for (var i = 0; i < range.length; i++) {
			data1.push({
				x: range[i],
				y: leadlag(range[i])
			});

			data2.push({
				x: range[i],
				y: mag2db(math.abs(leadlag(range[i])))
			});
			
			data3.push({
				x: range[i],
				y: rad2deg(angle(leadlag(range[i])))
			});
		}

		series.push({data: data2, width: 1, color: 'blue', stroke: "0,0", legend: "MAG"  });

		var series = plotMagnitude.selectAll(".line").data(series);

		series.enter().append('path');

		series.attr("class","line")
			.attr("d",function(d) { return line(d.data); })
			.attr("stroke-width", function(d) { return d.width; })
			.style("stroke", function(d) { return d.color; })
			.style("stroke-dasharray", function(d) { return d.stroke; });
	}

	$(function() {
		redraw();
	});
</script>
</body>
</html>

1 个答案:

答案 0 :(得分:1)

似乎您的“主要”刻度被定义为具有标签的刻度,因此难以找到它们然后找到相应的网格线。

怎么样:

d3.selectAll('.x1.axis>.tick')  // find all the x axis ticks and loop
  .each(function(d,i){
    if (d3.select(this).select('text').text() === ""){ // if they have no label
      d3.select('.x.grid>.tick:nth-child(' + (i + 1) + ')') // get the corresponding grid line
        .style("stroke-dasharray", "3,3"); // and make it dashed
      }
  });

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<title>LOG</title>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//mathjs.org/js/lib/math.js"></script>
<style type="text/css">
svg {
    font: 10px sans-serif;
    shape-rendering: crispEdges;
}
  
rect {
    fill: transparent;
}
  
.axis path,
.axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}
  
.line {
    fill: none;
    stroke: steelblue;
    stroke-width: 1.5px;
    clip-path: url(#clip);
}
  
.grid .tick {
    stroke: lightgrey;
    opacity: 0.7;
}
  
.grid path {
    stroke-width: 0;
}
</style>
<script type="text/javascript">
	function linspace(a,b,n) {
		var every = (b-a)/(n-1),
			range = [];
		for (i = a; i < b; i += every)
			range.push(i);
		return range.length == n ? range : range.concat(b);
	}
	
	function logspace(a,b,n) {
		return linspace(a,b,n).map(function(x) { return Math.pow(10,x); });
	}
	
	function isInteger(value) {
		return typeof value === "number" && 
			   isFinite(value) && 
			   Math.floor(value) === value;
	};
	
	function leadlag(f) {
		w = 2*math.pi*f;
		s = math.complex(0,w);

		K  = 1;
		fz = 20;
		fp = 40;

		wz = 2*math.pi*fz;
		wp = 2*math.pi*fp;

		return math.multiply(K,math.multiply(math.divide(wp,wz),math.divide(math.add(s,wz),(math.add(s,wp)))));
	};
	
	function angle(f) {
		return math.atan2(f.im,f.re);
	};
	
	function deg2rad(deg) {
  		return deg * math.pi / 180;
	};
 
	function rad2deg(rad) {
  		return rad * 180 / math.pi;
	};
	
	function mag2db(mag) {
		return 20 * Math.log10(mag);
	}
	
	function db2mag(db) {
		return math.pow(10,db / 20);
	}
</script>
</head>
<body>
<div id="plotmagnitude"></div>
<div id="plotphase"></div>
<script type="text/javascript">
	var margin = {
        top: 20,
        right: 20,
        bottom: 35,
        left: 50
    };
	
	var width  = 450 - margin.left - margin.right;
	var height = 250 - margin.top - margin.bottom;
	
	var range = logspace(0,3,1000);

    var x = d3.scale.log()
		.domain([1, range[range.length-1].toFixed()])
		.range([0, width]);

    var y = d3.scale.linear()
		.domain([-10, 2])
		.range([height, 0]);

    var xAxis1 = d3.svg.axis()
		.scale(x)
		.orient("bottom")
		.ticks(1,"0.1s")
		.innerTickSize(-6)
		.outerTickSize(0)
		.tickPadding(7);

    var yAxis1 = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.innerTickSize(-6)
		.outerTickSize(0)
		.tickPadding(7);

    var xAxis2 = d3.svg.axis()
		.scale(x)
		.orient("top")
		.ticks(5)
		.innerTickSize(-6)
		.tickPadding(-20)
		.outerTickSize(0)
		.tickFormat("");

    var yAxis2 = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.innerTickSize(6)
		.tickPadding(-20)
		.outerTickSize(0)
		.tickFormat("");

    var xGrid = d3.svg.axis()
		.scale(x)
		.orient("bottom")
		.ticks(5)
		.tickSize(-height, -height, 0)
		.tickFormat("");

    var yGrid = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.tickSize(-width, -width, 0)
		.tickFormat("");
	
    var line = d3.svg.line()
		.x(function(d) {
			return x(d.x);
		})
		.y(function(d) {
			return y(d.y);
		})
		.interpolate("linear");
	  
    var zoom = d3.behavior.zoom()
		.x(x)
		.y(y)
		.scaleExtent([1, 1])
		.on("zoom",redraw);
      
    var plotMagnitude = d3.select("#plotmagnitude").append("svg")
		.attr("width",width + margin.left + margin.right)
		.attr("height",height + margin.top + margin.bottom)
		.append("g")
		.attr("transform","translate(" + margin.left + "," + margin.top + ")")
		.call(zoom);
	  
    // Add x grid
    plotMagnitude.append("g")
		.attr("class","x grid")
		.attr("transform","translate(0," + height + ")")
		.call(xGrid);

    // Add y grid
    plotMagnitude.append("g")
		.attr("class","y grid")
		.call(yGrid);

    plotMagnitude.append("g")
		.attr("class","x1 axis")
		.attr("transform","translate(0," + height + ")")
		.call(xAxis1);

    plotMagnitude.append("g")
		.attr("class","y1 axis")
		.call(yAxis1);

    /* append additional X axis */
    plotMagnitude.append("g")
		.attr("class","x2 axis")
		.attr("transform","translate(" + [0, 0] + ")")
		.call(xAxis2);

    /* append additional y axis */
    plotMagnitude.append("g")
		.attr("class","y2 axis")
		.attr("transform","translate(" + [width, 0] + ")")
		.call(yAxis2);

    // Add x axis label  
    plotMagnitude.append("text")
        .attr("transform","translate(" + (width / 2) + "," + (height + margin.bottom) + ")")
        .style("font-size","15")
        .style("text-anchor","middle")
        .text("x axis");

    // Add y axis label
    plotMagnitude.append("text")
        .attr("transform", "rotate(-90)")
        .attr("y",0 - margin.left)
        .attr("x",0 - (height / 2))
        .attr("dy", "1em")
        .style("font-size","15")
        .style("text-anchor", "middle")
        .text("y axis");

    plotMagnitude.append("defs").append("clipPath")
		.attr("id", "clip")
		.append("rect")
		.attr("width", width)
		.attr("height", height);
      
    plotMagnitude.append("rect")
		.attr("width", width)
		.attr("height", height);

    function redraw() {
		plotMagnitude.select(".x1.axis").call(xAxis1);
		plotMagnitude.select(".y1.axis").call(yAxis1);
		plotMagnitude.select(".x2.axis").call(xAxis2);
		plotMagnitude.select(".y2.axis").call(yAxis2);
		plotMagnitude.select(".x.grid").call(xGrid);
		plotMagnitude.select(".y.grid").call(yGrid);
		
		var series = [];
		var data1 = [];
		var data2 = [];
		var data3 = [];

		for (var i = 0; i < range.length; i++) {
			data1.push({
				x: range[i],
				y: leadlag(range[i])
			});

			data2.push({
				x: range[i],
				y: mag2db(math.abs(leadlag(range[i])))
			});
			
			data3.push({
				x: range[i],
				y: rad2deg(angle(leadlag(range[i])))
			});
		}

		series.push({data: data2, width: 1, color: 'blue', stroke: "0,0", legend: "MAG"  });

		var series = plotMagnitude.selectAll(".line").data(series);

		series.enter().append('path');

		series.attr("class","line")
			.attr("d",function(d) { return line(d.data); })
			.attr("stroke-width", function(d) { return d.width; })
			.style("stroke", function(d) { return d.color; })
			.style("stroke-dasharray", function(d) { return d.stroke; });
			
		d3.selectAll('.x1.axis>.tick')
		  .each(function(d,i){
  		  if (d3.select(this).select('text').text() === ""){
  		    d3.select('.x.grid>.tick:nth-child(' + (i + 1) + ')')
  		      .style("stroke-dasharray", "3,3");
  		  }
		  });
		
	}

	$(function() {
		redraw();
	});
</script>
</body>
</html>