如何解决使用 d3-geo 在“月球的黑暗面”渲染
在 d3-geo 中使用正交投影,我希望能够在投影球体的不可见一侧渲染点。具体来说,我正在制作一个动画,其中一系列点围绕子午线移动,但是当它们到达边缘时,就好像它们会掉下来(当然,直到它们到达另一侧)。请参阅下面的代码片段。
目前,如果您尝试在隐藏的点上对其进行评估,路径生成器(在我的示例中为 path
)只会返回 null
。这当然是它应该做的,但是是否可以对其进行调整,使其在前表面上为我提供等效点,以便我可以将其渲染为较浅的颜色,这样就好像我们正在查看一样球体?
let width = 400;
let points = [...Array(20).keys()].map(k => 2 * k).map(k => [k,50]);
let svg = d3.select("#d3-example")
.attr("height",400)
.attr("width",width)
.style("shape-rendering","crispEdges");
let projection = d3.geoOrthographic()
.rotate([0,-30])
projection
.translate([width / 2,150])
.scale(width / 960 * projection.scale())
let path = d3.geoPath().projection(projection);
svg.append("path")
.attr("d",path(d3.geoGraticule10()))
.attr("stroke","#AAAAAA")
.attr("stroke-width",0.5)
.attr("fill","none");
let pt = svg.append("g")
.attr("id","loc-points")
.selectAll("path")
.data(points)
.enter()
.append("path")
.attr("d",d => path(convertPointToGeoJSON(d)))
.attr("stroke","black")
.attr("fill","black")
.attr("opacity",(d,i) => (i + 1) / 20)
.attr("stroke-width",1)
pt.transition()
.duration(5000)
.ease(d3.easeLinear)
.on("start",function repeat() {
d3.active(this)
.attrTween("d",d => {
return t => {
return pointpos(t)(d)
}
})
.transition()
.on("start",repeat)
})
function pointpos(t) {
return function(d) {
return path(convertPointToGeoJSON([d[0] + 360 * t,d[1]]))
}
}
function convertPointToGeoJSON(point) {
return {
type: "Point",coordinates: [point[0],point[1]]
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="d3-example"></svg>
解决方法
一种非常懒惰和愚蠢的方法是在不剪裁任何内容的情况下创建另一个投影(使用 projection.clipAngle(0)
),然后根据原始投影更改笔触和填充。
这是一个演示(我将圆圈移近赤道,以便更容易在隐藏的一侧看到它们):
let width = 400;
let points = [...Array(20).keys()].map(k => 2 * k).map(k => [k,20]);
let svg = d3.select("#d3-example")
.attr("height",400)
.attr("width",width)
.style("shape-rendering","crispEdges");
let projection = d3.geoOrthographic()
.rotate([0,-30])
let projection2 = d3.geoOrthographic()
.rotate([0,-30])
.clipAngle(0)
projection
.translate([width / 2,150])
.scale(width / 960 * projection.scale())
projection2
.translate([width / 2,150])
.scale(width / 960 * projection2.scale())
let path = d3.geoPath().projection(projection);
let path2 = d3.geoPath().projection(projection2);
svg.append("path")
.attr("d",path(d3.geoGraticule10()))
.attr("stroke","#AAAAAA")
.attr("stroke-width",0.5)
.attr("fill","none");
let pt = svg.append("g")
.attr("id","loc-points")
.selectAll("path")
.data(points)
.enter()
.append("path")
.attr("d",d => path2(convertPointToGeoJSON(d)))
.attr("stroke","black")
.attr("fill","black")
.attr("opacity",(d,i) => (i + 1) / 20)
.attr("stroke-width",1)
pt.transition()
.duration(5000)
.ease(d3.easeLinear)
.on("start",function repeat() {
d3.active(this)
.styleTween("fill",d => {
return t => {
return pointpos(t)(d) ? "black" : "#ccc"
}
})
.styleTween("stroke",d => {
return t => {
return pointpos(t)(d) ? "black" : "#aaa"
}
})
.attrTween("d",d => {
return t => {
return pointpos2(t)(d)
}
})
.transition()
.on("start",repeat)
})
function pointpos(t) {
return function(d) {
return path(convertPointToGeoJSON([d[0] + 360 * t,d[1]]))
}
}
function pointpos2(t) {
return function(d) {
return path2(convertPointToGeoJSON([d[0] + 360 * t,d[1]]))
}
}
function convertPointToGeoJSON(point) {
return {
type: "Point",coordinates: [point[0],point[1]]
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="d3-example"></svg>
我相信很快就会有人发布合适的解决方案。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。