- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我对由 @TilingBot 生成的双曲曲面分割很感兴趣.为了进一步缩小范围,我希望能够构建一些 Uniform Tilings on the Hyperbolic Plane ,例如:
我找到的最接近的答案来自 Math SE并推荐这 3 个资源:
这里我已经将 Java 翻译成 JavaScript(并保留了注释),并尝试绘制中心形状:
class Polygon {
constructor(n) {
this.n = n // the number of sides
this.v = new Array(n) // the list of vertices
}
static constructCenterPolygon(n, k, { quasiregular = false }) {
// Initialize P as the center polygon in an n-k regular or quasiregular tiling.
// Let ABC be a triangle in a regular (n,k0-tiling, where
// A is the center of an n-gon (also center of the disk),
// B is a vertex of the n-gon, and
// C is the midpoint of a side of the n-gon adjacent to B.
const angleA = Math.PI / n
const angleB = Math.PI / k
const angleC = Math.PI / 2.0
// For a regular tiling, we need to compute the distance s from A to B.
let sinA = Math.sin(angleA)
let sinB = Math.sin(angleB)
let s = Math.sin(angleC - angleB - angleA)
/ Math.sqrt(1.0 - (sinB * sinB) - (sinA * sinA))
// But for a quasiregular tiling, we need the distance s from A to C.
if (quasiregular) {
s = ((s * s) + 1.0) / (2.0 * s * Math.cos(angleA))
s = s - Math.sqrt((s * s) - 1.0)
}
// Now determine the coordinates of the n vertices of the n-gon.
// They're all at distance s from the center of the Poincare disk.
const polygon = new Polygon(n)
for (let i = 0; i < n; ++i) {
const something = (3 + 2 * i) * angleA
const x = s * Math.cos(something)
const y = s * Math.sin(something)
const point = new Point(x, y)
polygon.v[i] = point
}
return polygon
}
getScreenCoordinateArrays(dimension) {
// first construct a list of all the points
let pointList = null
for (let i = 0; i < this.n; ++i) {
const next = (i + 1) % this.n
const line = new Line(this.v[i], this.v[next])
pointList = line.appendScreenCoordinates(pointList, dimension)
}
// determine its length
let _in = 0
for (let pl = pointList; pl != null; pl = pl.link) {
_in++
}
// now store the coordinates
let pl = pointList
let ix = []
let iy = []
for (let i = 0; i < _in; i++) {
ix[i] = pl.x
iy[i] = pl.y
pl = pl.link
}
return { size: _in, ix, iy }
}
}
class Line {
constructor(a, b) {
this.a = a // this is the line between a and b
this.b = b
// if it's a circle, then a center C and radius r are needed
this.c = null
this.r = null
// if it's is a straight line, then a point P and a direction D
// are needed
this.p = null
this.d = null
// first determine if its a line or a circle
let den = (a.x * b.y) - (b.x * a.y)
this.isStraight = (Math.abs(den) < 1.0e-14)
if (this.isStraight) {
this.p = a; // a point on the line
// find a unit vector D in the direction of the line
den = Math.sqrt(
(a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)
)
let x = (b.x - a.x) / den
let y = (b.y - a.y) / den
this.d = new Point(x, y)
} else { // it's a circle
// find the center of the circle thru these points}
let s1 = (1.0 + (a.x * a.x) + (a.y * a.y)) / 2.0
let s2 = (1.0 + (b.x * b.x) + (b.y * b.y)) / 2.0
let x = (s1 * b.y - s2 * a.y) / den
let y = (a.x * s2 - b.x * s1) / den
this.c = new Point (x, y)
this.r = Math.sqrt(
(this.c.x * this.c.x)
+ (this.c.y * this.c.y)
- 1.0
)
}
}
// Reflect the point R thru the this line
// to get Q the returned point
reflect(point) {
reflection = new Point()
if (this.isStraight) {
const factor = 2.0 * (
(point.x - this.p.x)
* this.d.x
+ (point.y - this.p.y)
* this.d.y
)
reflection.x = 2.0 * this.p.x + factor * this.d.x - point.x
reflection.y = 2.0 * this.p.y + factor * this.d.y - point.y
} else { // it's a circle
const factor = (r * r) / (
(point.x - this.c.x) * (point.x - this.c.x)
+ (point.y - this.c.y) * (point.y - this.c.y)
)
reflection.x = this.c.x + factor * (point.x - this.c.x)
reflection.y = this.c.y + factor * (point.y - this.c.y)
}
return reflection
}
// append screen coordinates to the list in order to draw the line
appendScreenCoordinates(list, dimension) {
let x_center = dimension.width / 2
let y_center = dimension.height / 2
let radius = Math.min(x_center, y_center)
let x = Math.round((this.a.x * radius) + x_center)
let y = Math.round((this.a.y * radius) + y_center)
const conditionA = (list == null || x != list.x || y != list.y)
const conditionB = !isNaN(x) && !isNaN(y)
if (conditionA && conditionB) {
list = new ScreenCoordinateList(list, x, y)
}
if (this.isStraight) { // go directly to terminal point B
x = Math.round((this.b.x * radius) + x_center)
y = Math.round((this.b.y * radius) + y_center)
const conditionC = x != list.x || y != list.y
if (conditionC) {
list = new ScreenCoordinateList(list, x, y)
}
} else { // its an arc of a circle
// determine starting and ending angles
let alpha = Math.atan2(
this.a.y - this.c.y,
this.a.x - this.c.x
)
let beta = Math.atan2(
this.b.y - this.c.y,
this.b.x - this.c.x
)
if (Math.abs(beta - alpha) > Math.PI) {
if (beta < alpha) {
beta += (2.0 * Math.PI)
}
} else {
alpha += (2.0 * Math.PI)
}
const curve = new CircularCurve(this.c.x, this.c.y, this.r)
curve.setScreen(x_center, y_center, radius)
list = curve.interpolate(list, alpha, beta)
}
return list;
}
draw(dimensions) {
let x_center = dimensions.width / 2
let y_center = dimensions.height / 2
let radius = Math.min(x_center, y_center)
// yet to write...
}
}
class CircularCurve {
// The circle in the complex plane
constructor(x, y, r) {
// coordinates of the center of the circle
this.x = x
this.y = y
this.r = r // radius of the circle
}
// Add to the list the coordinates of the curve (f(t),g(t)) for t
// between a and b. It is assumed that the point (f(a),g(a)) is
// already on the list. Enough points will be interpolated between a
// and b so that the approximating polygon looks like the curve.
// The last point to be included will be (f(b),g(b)).}
interpolate(list, a, b) {
// first try bending it at the midpoint
let result = this.bent(a, b, (a + b) / 2.0, list)
if (result != list) return result
// now try 4 random points
for (let i = 0; i < 4; ++i) {
const t = Math.random()
result = this.bent(a, b, (t * a) + ((1.0 - t) * b), list)
if (result != list) return result
}
// it's a straight line
const b1 = this.xScreen(b)
const b2 = this.yScreen(b)
const conditionA = (list.x != b1 || list.y != b2)
const conditionB = !isNaN(b1) && !isNaN(b2)
if (conditionA && conditionB) {
list = new ScreenCoordinateList(list, b1, b2)
}
return list // it's a straight line
}
// Determine if a curve between t=a and t=b is bent at t=c.
// Say it is if C is outside a narrow ellipse.
// If it is bent there, subdivide the interval.
bent(a, b, c, list) {
const a1 = this.xScreen(a)
const a2 = this.yScreen(a)
const b1 = this.xScreen(b)
const b2 = this.yScreen(b)
const c1 = this.xScreen(c)
const c2 = this.yScreen(c)
const excess =
Math.sqrt((a1 - c1) * (a1 - c1) + (a2 - c2) * (a2 - c2))
+ Math.sqrt((b1 - c1) * (b1 - c1) + (b2 - c2) * (b2 - c2))
- Math.sqrt((a1 - b1) * (a1 - b1) + (a2 - b2) * (a2 - b2))
if (excess > 0.03) {
list = this.interpolate(list, a, c)
list = this.interpolate(list, c, b)
}
return list
}
setScreen(x_center, y_center, radius) {
// screen coordinates
this.x_center = x_center // x-coordinate of the origin
this.y_center = y_center // y-coordinate of the origin
this.radius = radius // distance to unit circle
}
xScreen(t) {
return Math.round(this.x_center + (this.radius * this.getX(t)))
}
yScreen(t) {
return Math.round(this.y_center + (this.radius * this.getY(t)))
}
getX(t) {
return this.x + (this.r * Math.cos(t))
}
getY(t) {
return this.y + (this.r * Math.sin(t))
}
}
class ScreenCoordinateList {
constructor(link, x, y) {
// link to next one
this.link = link
// coordinate pair
this.x = x
this.y = y
}
}
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
}
body {
padding: 50px;
display: flex;
justify-content: center;
align-items: center;
}
<canvas width="1000" height="1000"></canvas>
<script>
window.addEventListener('load', draw)
function draw() {
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const polygon = Polygon.constructCenterPolygon(7, 3, {quasiregular: true
})
const { size, ix, iy } = polygon.getScreenCoordinateArrays({
width: 100,
height: 100
})
ctx.fillStyle = '#af77e7'
ctx.beginPath()
ctx.moveTo(ix[0], iy[0])
let i = 1
while (i < size) {
ctx.lineTo(ix[i], iy[i])
i++
}
ctx.closePath()
ctx.fill()
}
</script>
对于这个 n
,我怎样才能同时绘制中心形状和从中心向外绘制 1 或两层(或者 {7,3}
从中心向外绘制层,如果它很简单的话) HTML5 Canvas 上 JavaScript 中的双曲线曲面分割?
我目前得到这个:
理想情况下,我想绘制上面附加的第一个双曲线镶嵌图像,但如果考虑到我目前从 David Joyce 的作品中得到的东西,这太复杂了,那么第一步是计算中心多边形并用填充和线条正确吗?
最佳答案
我建议您使用描边而不是填充,这样您就可以看到多边形给您带来的效果。
运行下面的代码,以便您可以看到不同之处...
现在,将该结果与您的图像进行比较,它看起来与您想要的完全不同
class Polygon {
constructor(n) {
this.n = n // the number of sides
this.v = new Array(n) // the list of vertices
}
static constructCenterPolygon(n, k, { quasiregular = false }) {
// Initialize P as the center polygon in an n-k regular or quasiregular tiling.
// Let ABC be a triangle in a regular (n,k0-tiling, where
// A is the center of an n-gon (also center of the disk),
// B is a vertex of the n-gon, and
// C is the midpoint of a side of the n-gon adjacent to B.
const angleA = Math.PI / n
const angleB = Math.PI / k
const angleC = Math.PI / 2.0
// For a regular tiling, we need to compute the distance s from A to B.
let sinA = Math.sin(angleA)
let sinB = Math.sin(angleB)
let s = Math.sin(angleC - angleB - angleA)
/ Math.sqrt(1.0 - (sinB * sinB) - (sinA * sinA))
// But for a quasiregular tiling, we need the distance s from A to C.
if (quasiregular) {
s = ((s * s) + 1.0) / (2.0 * s * Math.cos(angleA))
s = s - Math.sqrt((s * s) - 1.0)
}
// Now determine the coordinates of the n vertices of the n-gon.
// They're all at distance s from the center of the Poincare disk.
const polygon = new Polygon(n)
for (let i = 0; i < n; ++i) {
const something = (3 + 2 * i) * angleA
const x = s * Math.cos(something)
const y = s * Math.sin(something)
const point = new Point(x, y)
polygon.v[i] = point
}
return polygon
}
getScreenCoordinateArrays(dimension) {
// first construct a list of all the points
let pointList = null
for (let i = 0; i < this.n; ++i) {
const next = (i + 1) % this.n
const line = new Line(this.v[i], this.v[next])
pointList = line.appendScreenCoordinates(pointList, dimension)
}
// determine its length
let _in = 0
for (let pl = pointList; pl != null; pl = pl.link) {
_in++
}
// now store the coordinates
let pl = pointList
let ix = []
let iy = []
for (let i = 0; i < _in; i++) {
ix[i] = pl.x
iy[i] = pl.y
pl = pl.link
}
return { size: _in, ix, iy }
}
}
class Line {
constructor(a, b) {
this.a = a // this is the line between a and b
this.b = b
// if it's a circle, then a center C and radius r are needed
this.c = null
this.r = null
// if it's is a straight line, then a point P and a direction D
// are needed
this.p = null
this.d = null
// first determine if its a line or a circle
let den = (a.x * b.y) - (b.x * a.y)
this.isStraight = (Math.abs(den) < 1.0e-14)
if (this.isStraight) {
this.p = a; // a point on the line
// find a unit vector D in the direction of the line
den = Math.sqrt(
(a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)
)
let x = (b.x - a.x) / den
let y = (b.y - a.y) / den
this.d = new Point(x, y)
} else { // it's a circle
// find the center of the circle thru these points}
let s1 = (1.0 + (a.x * a.x) + (a.y * a.y)) / 2.0
let s2 = (1.0 + (b.x * b.x) + (b.y * b.y)) / 2.0
let x = (s1 * b.y - s2 * a.y) / den
let y = (a.x * s2 - b.x * s1) / den
this.c = new Point (x, y)
this.r = Math.sqrt(
(this.c.x * this.c.x)
+ (this.c.y * this.c.y)
- 1.0
)
}
}
// Reflect the point R thru the this line
// to get Q the returned point
reflect(point) {
reflection = new Point()
if (this.isStraight) {
const factor = 2.0 * (
(point.x - this.p.x)
* this.d.x
+ (point.y - this.p.y)
* this.d.y
)
reflection.x = 2.0 * this.p.x + factor * this.d.x - point.x
reflection.y = 2.0 * this.p.y + factor * this.d.y - point.y
} else { // it's a circle
const factor = (r * r) / (
(point.x - this.c.x) * (point.x - this.c.x)
+ (point.y - this.c.y) * (point.y - this.c.y)
)
reflection.x = this.c.x + factor * (point.x - this.c.x)
reflection.y = this.c.y + factor * (point.y - this.c.y)
}
return reflection
}
// append screen coordinates to the list in order to draw the line
appendScreenCoordinates(list, dimension) {
let x_center = dimension.width / 2
let y_center = dimension.height / 2
let radius = Math.min(x_center, y_center)
let x = Math.round((this.a.x * radius) + x_center)
let y = Math.round((this.a.y * radius) + y_center)
const conditionA = (list == null || x != list.x || y != list.y)
const conditionB = !isNaN(x) && !isNaN(y)
if (conditionA && conditionB) {
list = new ScreenCoordinateList(list, x, y)
}
if (this.isStraight) { // go directly to terminal point B
x = Math.round((this.b.x * radius) + x_center)
y = Math.round((this.b.y * radius) + y_center)
const conditionC = x != list.x || y != list.y
if (conditionC) {
list = new ScreenCoordinateList(list, x, y)
}
} else { // its an arc of a circle
// determine starting and ending angles
let alpha = Math.atan2(
this.a.y - this.c.y,
this.a.x - this.c.x
)
let beta = Math.atan2(
this.b.y - this.c.y,
this.b.x - this.c.x
)
if (Math.abs(beta - alpha) > Math.PI) {
if (beta < alpha) {
beta += (2.0 * Math.PI)
}
} else {
alpha += (2.0 * Math.PI)
}
const curve = new CircularCurve(this.c.x, this.c.y, this.r)
curve.setScreen(x_center, y_center, radius)
list = curve.interpolate(list, alpha, beta)
}
return list;
}
draw(dimensions) {
let x_center = dimensions.width / 2
let y_center = dimensions.height / 2
let radius = Math.min(x_center, y_center)
// yet to write...
}
}
class CircularCurve {
// The circle in the complex plane
constructor(x, y, r) {
// coordinates of the center of the circle
this.x = x
this.y = y
this.r = r // radius of the circle
}
// Add to the list the coordinates of the curve (f(t),g(t)) for t
// between a and b. It is assumed that the point (f(a),g(a)) is
// already on the list. Enough points will be interpolated between a
// and b so that the approximating polygon looks like the curve.
// The last point to be included will be (f(b),g(b)).}
interpolate(list, a, b) {
// first try bending it at the midpoint
let result = this.bent(a, b, (a + b) / 2.0, list)
if (result != list) return result
// now try 4 random points
for (let i = 0; i < 4; ++i) {
const t = Math.random()
result = this.bent(a, b, (t * a) + ((1.0 - t) * b), list)
if (result != list) return result
}
// it's a straight line
const b1 = this.xScreen(b)
const b2 = this.yScreen(b)
const conditionA = (list.x != b1 || list.y != b2)
const conditionB = !isNaN(b1) && !isNaN(b2)
if (conditionA && conditionB) {
list = new ScreenCoordinateList(list, b1, b2)
}
return list // it's a straight line
}
// Determine if a curve between t=a and t=b is bent at t=c.
// Say it is if C is outside a narrow ellipse.
// If it is bent there, subdivide the interval.
bent(a, b, c, list) {
const a1 = this.xScreen(a)
const a2 = this.yScreen(a)
const b1 = this.xScreen(b)
const b2 = this.yScreen(b)
const c1 = this.xScreen(c)
const c2 = this.yScreen(c)
const excess =
Math.sqrt((a1 - c1) * (a1 - c1) + (a2 - c2) * (a2 - c2))
+ Math.sqrt((b1 - c1) * (b1 - c1) + (b2 - c2) * (b2 - c2))
- Math.sqrt((a1 - b1) * (a1 - b1) + (a2 - b2) * (a2 - b2))
if (excess > 0.03) {
list = this.interpolate(list, a, c)
list = this.interpolate(list, c, b)
}
return list
}
setScreen(x_center, y_center, radius) {
// screen coordinates
this.x_center = x_center // x-coordinate of the origin
this.y_center = y_center // y-coordinate of the origin
this.radius = radius // distance to unit circle
}
xScreen(t) {
return Math.round(this.x_center + (this.radius * this.getX(t)))
}
yScreen(t) {
return Math.round(this.y_center + (this.radius * this.getY(t)))
}
getX(t) {
return this.x + (this.r * Math.cos(t))
}
getY(t) {
return this.y + (this.r * Math.sin(t))
}
}
class ScreenCoordinateList {
constructor(link, x, y) {
// link to next one
this.link = link
// coordinate pair
this.x = x
this.y = y
}
}
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
}
<canvas width="400" height="400"></canvas>
<script>
window.addEventListener('load', draw)
function draw() {
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
ctx.translate(150, 150);
const polygon = Polygon.constructCenterPolygon(7, 3, {quasiregular: true})
const { size, ix, iy } = polygon.getScreenCoordinateArrays({width: 80, height: 80})
for (i = 0; i < size; i++) {
ctx.lineTo(ix[i], iy[i])
}
ctx.stroke()
}
</script>
但是如果您要解决的问题是:
what would be the first step, of computing the center polygon and drawing it
那个中心多边形看起来像一个等边多边形,代码可以更简单,见下文
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
function drawEquilateralPolygon(x, y, lines, size) {
ctx.beginPath();
for (angle = 0; angle < 360; angle += 360 / lines) {
a = angle * Math.PI / 180
ctx.lineTo(x + size * Math.sin(a), y + size * Math.cos(a));
}
ctx.lineTo(x + size * Math.sin(0), y + size * Math.cos(0));
ctx.stroke();
}
drawEquilateralPolygon(20, 20, 5, 20)
drawEquilateralPolygon(60, 50, 6, 30)
drawEquilateralPolygon(130, 70, 7, 40)
drawEquilateralPolygon(200, 35, 8, 30)
drawEquilateralPolygon(200, 35, 9, 25)
drawEquilateralPolygon(200, 35, 10, 35)
<canvas id="c"></canvas>
关于javascript - 如何在给定 JavaScript 中的 Schläfli 符号的庞加莱圆盘上绘制双曲曲面分割?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64060851/
给定一个字符串,例如 s="##$$$#",我如何找到索引之前的“#”符号数等于“”数的索引$"符号在索引之后? 示例:如果 s="##$$$#",则输出将为 2。 解释:在索引 2 之前我们有 2
在本教程中,您将借助示例了解 JavaScript 符号。 JavaScript 符号 JavaScript ES6 引入了一种新的原始数据类型,称为 Symbol(符号)。符号是不可变的(不能更改)
在“函数编程的工艺”一书中,符号 '>.>' 将函数连接在一起,与 '.' 的方向相反。但是当我使用 ghci 实现它时,它显示了超出范围的错误 '>.>'。为什么?它是不再使用的旧符号吗? 最佳答案
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我需要从向量中删除 \"。这是我的数据: data <- c("\"https://click.linksynergy.com/link?id=RUxZriH*PWc&offerid=323058.1
我在 Nginx 配置中使用正则表达式来捕获文件 URL,但如果文件 URL 包含 # 符号,正则表达式模式将不会捕获它。 这里是nginx的配置部分。 location ~ ^/p/(?[\w\-=
如何使 & 符号在此图表的第一组条形/列下正确显示: http://jsfiddle.net/VxbrK/2/ 应该是“Apples & Oranges”而不是“Apples & Oranges”。
**在verilog中是什么意思? 我为测试台提供了以下逻辑 localparam NUM_INPUT_BITS = 1; localparam NUM_OUTPUT_BITS
我有一个使用正则表达式来验证电子邮件地址的方法。 public String searchFormail(String searchWord) { Pattern pattern = Patt
我想将一个字符串拆分为数字部分和文本/符号部分我当前的代码不包含负数或小数,并且表现得很奇怪,在输出的末尾添加了一个空列表元素 import re mystring = 'AD%5(6ag 0.33-
我有一些代码需要从数组中选择一个随机字符串,但它一直返回单个字母或数字。如何解决这个问题? var name = ["Yayek", "Vozarut", "Gezex",
我刚开始使用 Python,我在考虑应该使用哪种表示法。我读过 PEP 8关于 Python 符号的指南,我同意那里的大多数内容,除了函数名称(我更喜欢混合大小写风格)。 在 C++ 中,我使用匈牙利
在用 C# 编写代码时,我错误地在 if 语句中的变量前添加了一个符号(而不是感叹号)。 bool b = false; if (@b) { } 我很惊讶它编译成功,没有任何错误。 我想知道:上面的代
本文实例为大家分享了特殊字符替换电话号码中某一部分的方法,ios利用-号替换电话号码中间四位,供大家参考,具体内容如下 1、效果图 2、代码 rootviewcontroll
当我使用“x”和“z”作为符号时,这段代码没有问题: from sympy import * x, z = symbols('x z') y = -6*x**2 + 2*x*z**0.5 + 50*x
我需要从文本中删除标点符号: data <- "Type the command AT&W enter. in order to save the new protocol on modem;"
我有几个数字是 numeric 类。下面的例子。 df = c(12974,12412,124124,124124,34543,4576547,32235) 现在我想在每个数字前添加 '$' 符号而不
我有一个 highcharts 图例,其中符号以不同的大小显示,因为它们在实际图表中的大小不同。不幸的是,当数据点的大小增加时,它们也会在图例中增加。无论数据点大小如何,我都希望图例符号保持相同的大小
我需要使用包含平均值+-SD的标题。到目前为止,我只能得到以下信息: "Mean +- SD or N (%)" [1] "Mean +- SD or N (%)" 如何直接使用“+-”符号?您知道一
使用 XSLT 和 XPath 1.0,我有一个要转义的字符串以用于 URL,例如: one word & another 因此,描述元素的 text() 应该进行 URL 转义。 我该怎么做
我是一名优秀的程序员,十分优秀!