问一个代码问题。

import taichi as ti
import math

ti.init(ti.cpu)

Color = ti.types.vector(3, ti.f32)
Point = ti.types.vector(2, ti.i32)

width = 1024
height = 1024
pixels = Color.field(shape=(width, height))
center = Point([width // 2, height // 2])


def BresenhamLine(P0, P1):
    dx = ti.abs(P1.x - P0.x)
    dy = ti.abs(P1.y - P0.y)
    bInterchange = False
    signX = 1 if P1.x > P0.x else -1 if P1.x < P0.x else 0
    signY = 1 if P1.y > P0.y else -1 if P1.y < P0.y else 0
    if dy >= dx:
        dx, dy = dy, dx
        bInterchange = True
    e = -dx
    x = P0.x
    y = P0.y
    color = Color([0.0, 0.0, 0.0])
    for i in range(1, int(dx) + 1):
        pixels[x, y] = color
        if not bInterchange:
            x += signX
        else:
            y += signY
        e += 2 * dy
        if e >= 0:
            if not bInterchange:
                y += signY
            else:
                x += signX
            e -= 2 * dx


class Pentagram:
    def __init__(self, C, R):
        self.C = C
        self.R = R
        self.P = Point.field(shape=(5))

    def ReadPoint(self):
        Theta = 2 * math.pi / 5
        Alpha = math.pi / 2 - Theta
        for i in range(5):
            x = self.R * ti.cos(i * Theta + Alpha)
            y = self.R * ti.sin(i * Theta + Alpha)
            self.P[i] = self.C + Point([x, y])

    def Rotate(self, angle):
        M = ti.Matrix([
            [ti.cos(angle * math.pi / 180), -ti.sin(angle * math.pi / 180)],
            [ti.sin(angle * math.pi / 180), ti.cos(angle * math.pi / 180)],
        ])
        for i in range(5):
            t = M @ (self.P[i] - self.C) + self.C
            self.P[i].x = int(t.x)
            self.P[i].y = int(t.y)

    def Draw(self):
        BresenhamLine(self.P[0], self.P[2])
        BresenhamLine(self.P[1], self.P[3])
        BresenhamLine(self.P[2], self.P[4])
        BresenhamLine(self.P[3], self.P[0])
        BresenhamLine(self.P[4], self.P[1])


C = Point([0, -200]) + center
R = 150
pentagram = Pentagram(C, R)
pentagram.ReadPoint()
gui = ti.GUI("15", (width, height))
while gui.running:
    pixels.fill(Color([1.0, 1.0, 1.0]))
    P0 = Point([-100, 0]) + center
    P1 = Point([100, 0]) + center
    BresenhamLine(P0, P1)
    P3 = Point([0, 0]) + center
    BresenhamLine(P3, C)
    pentagram.Rotate(5.0)
    pentagram.Draw()
    gui.set_image(pixels)
    gui.show()

其中,

    def Rotate(self, angle):
        M = ti.Matrix([
            [ti.cos(angle * math.pi / 180), -ti.sin(angle * math.pi / 180)],
            [ti.sin(angle * math.pi / 180), ti.cos(angle * math.pi / 180)],
        ])
        for i in range(5):
            t = M @ (self.P[i] - self.C) + self.C
            self.P[i].x = int(t.x)
            self.P[i].y = int(t.y)

给P不能直接赋值,错误信息:

(taichi) ➜  KLD-40 git:(master) ✗ /opt/miniconda3/envs/taichi/bin/python /Users/jiajiewu/Projects/KLD-40/算法15:二维几何变换算法/main.py
[Taichi] version 0.8.11, llvm 10.0.0, commit 9d0973e5, osx, python 3.9.7
[Taichi] Starting on arch=x64
Traceback (most recent call last):
  File "/Users/jiajiewu/Projects/KLD-40/算法15:二维几何变换算法/main.py", line 88, in <module>
    pentagram.Rotate(5.0)
  File "/Users/jiajiewu/Projects/KLD-40/算法15:二维几何变换算法/main.py", line 66, in Rotate
    self.P[i] = t
  File "/opt/miniconda3/envs/taichi/lib/python3.9/site-packages/taichi/lang/util.py", line 224, in wrapped
    return func(*args, **kwargs)
  File "/opt/miniconda3/envs/taichi/lib/python3.9/site-packages/taichi/lang/matrix.py", line 1244, in __setitem__
    self[key].set_entries(value)
  File "/opt/miniconda3/envs/taichi/lib/python3.9/site-packages/taichi/lang/util.py", line 224, in wrapped
    return func(*args, **kwargs)
  File "/opt/miniconda3/envs/taichi/lib/python3.9/site-packages/taichi/lang/matrix.py", line 393, in set_entries
    self[i, j] = value[i][j]
  File "/opt/miniconda3/envs/taichi/lib/python3.9/site-packages/taichi/lang/util.py", line 224, in wrapped
    return func(*args, **kwargs)
  File "/opt/miniconda3/envs/taichi/lib/python3.9/site-packages/taichi/lang/matrix.py", line 374, in __setitem__
    self.set_entry(i, j, item)
  File "/opt/miniconda3/envs/taichi/lib/python3.9/site-packages/taichi/lang/matrix.py", line 254, in set_entry
    self.entries[idx].accessor.setter(e, *self.entries[idx].key)
  File "/opt/miniconda3/envs/taichi/lib/python3.9/site-packages/taichi/lang/field.py", line 307, in setter
    snode.write_int(key, value)
TypeError: write_int(): incompatible function arguments. The following argument types are supported:
    1. (self: taichi._lib.core.taichi_core.SNode, arg0: List[int], arg1: int) -> None

Invoked with: <taichi._lib.core.taichi_core.SNode object at 0x7fc973e935f0>, (0, 0, 0, 0, 0, 0, 0, 0), 649.4504829626355

我感觉是类型问题,所以重新cast,再写上去。就ok了。
有个疑问,我把Point的定义改为ti.f32也是不可以,为什么呢?

我期待这样处理。

    def Rotate(self, angle):
        M = ti.Matrix([
            [ti.cos(angle * math.pi / 180), -ti.sin(angle * math.pi / 180)],
            [ti.sin(angle * math.pi / 180), ti.cos(angle * math.pi / 180)],
        ])
        for i in range(5):
            t = M @ (self.P[i] - self.C) + self.C
            #self.P[i].x = int(t.x)
            #self.P[i].y = int(t.y)
            self.P[i] = t

Hi @virgilwjj , 你期待的处理方法是可以的。只是需要注意两点:

  1. 在class中使用Taichi相关的功能,需要在class之前添加修饰器 @ti.data_oriented
  2. 在Taichi的function之前添加对应的修饰器,ti.kernel 或者 ti.func,需要注意ti.kernel中的参数是需要指定数据类型的。

我只修改了以上两点,就可以运行你的程序了。

import taichi as ti
import math

ti.init(ti.cpu)

Color = ti.types.vector(3, ti.f32)
Point = ti.types.vector(2, ti.i32)

width = 1024
height = 1024
pixels = Color.field(shape=(width, height))
center = Point([width // 2, height // 2])


def BresenhamLine(P0, P1):
    dx = ti.abs(P1.x - P0.x)
    dy = ti.abs(P1.y - P0.y)
    bInterchange = False
    signX = 1 if P1.x > P0.x else -1 if P1.x < P0.x else 0
    signY = 1 if P1.y > P0.y else -1 if P1.y < P0.y else 0
    if dy >= dx:
        dx, dy = dy, dx
        bInterchange = True
    e = -dx
    x = P0.x
    y = P0.y
    color = Color([0.0, 0.0, 0.0])
    for i in range(1, int(dx) + 1):
        pixels[x, y] = color
        if not bInterchange:
            x += signX
        else:
            y += signY
        e += 2 * dy
        if e >= 0:
            if not bInterchange:
                y += signY
            else:
                x += signX
            e -= 2 * dx

@ti.data_oriented
class Pentagram:
    def __init__(self, C, R):
        self.C = C
        self.R = R
        self.P = Point.field(shape=(5))

    def ReadPoint(self):
        Theta = 2 * math.pi / 5
        Alpha = math.pi / 2 - Theta
        for i in range(5):
            x = self.R * ti.cos(i * Theta + Alpha)
            y = self.R * ti.sin(i * Theta + Alpha)
            self.P[i] = self.C + Point([x, y])
    
    @ti.kernel 
    def Rotate(self, angle:  ti.f32):
        M = ti.Matrix([
            [ti.cos(angle * math.pi / 180), -ti.sin(angle * math.pi / 180)],
            [ti.sin(angle * math.pi / 180), ti.cos(angle * math.pi / 180)],
        ])
        for i in range(5):
            t = M @ (self.P[i] - self.C) + self.C
            self.P[i] = t

    def Draw(self):
        BresenhamLine(self.P[0], self.P[2])
        BresenhamLine(self.P[1], self.P[3])
        BresenhamLine(self.P[2], self.P[4])
        BresenhamLine(self.P[3], self.P[0])
        BresenhamLine(self.P[4], self.P[1])


C = Point([0, -200]) + center
R = 150
pentagram = Pentagram(C, R)
pentagram.ReadPoint()
gui = ti.GUI("15", (width, height))
while gui.running:
    pixels.fill(Color([1.0, 1.0, 1.0]))
    P0 = Point([-100, 0]) + center
    P1 = Point([100, 0]) + center
    BresenhamLine(P0, P1)
    P3 = Point([0, 0]) + center
    BresenhamLine(P3, C)
    pentagram.Rotate(5.0)
    pentagram.Draw()
    gui.set_image(pixels)
    gui.show()
3 个赞