mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
This commit is contained in:
1 parent
19dc79e8e3
commit
a710bc5f41
68 files changed
+2149
-724
No files matched your search
@@ -0,0 +1,120 @@
|
||||
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ofdgo
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// MeshSampler 保存独立网格快照和空间索引,支持并发采样
|
||||
type MeshSampler struct {
|
||||
mesh MeshShading
|
||||
nodes []meshSampleNode
|
||||
}
|
||||
|
||||
// meshSampleNode 保存连续三角形组的包围盒及右子树位置
|
||||
type meshSampleNode struct {
|
||||
minX, minY, maxX, maxY float64
|
||||
right int
|
||||
}
|
||||
|
||||
// NewSampler 创建网格快照,后续修改原网格不影响采样结果
|
||||
// 返回: *MeshSampler 只读采样器
|
||||
func (m *MeshShading) NewSampler() *MeshSampler {
|
||||
s := &MeshSampler{}
|
||||
if m != nil {
|
||||
s.mesh = *m
|
||||
s.mesh.Triangles = slices.Clone(m.Triangles)
|
||||
if len(m.Triangles) > 8 {
|
||||
s.build(0, len(m.Triangles))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// At 采样快照中的预乘颜色,后绘制的三角形优先
|
||||
// 入参: x 横坐标, y 纵坐标
|
||||
// 返回: color.RGBA 预乘颜色
|
||||
func (s *MeshSampler) At(x, y float64) color.RGBA {
|
||||
if len(s.nodes) == 0 || !finite(x) || !finite(y) {
|
||||
return s.mesh.At(x, y)
|
||||
}
|
||||
if value, ok := s.sample(0, 0, len(s.mesh.Triangles), x, y); ok {
|
||||
return value
|
||||
}
|
||||
return s.mesh.Background
|
||||
}
|
||||
|
||||
// build 按绘制顺序构建包围盒层级,不重排原三角形
|
||||
// 入参: first 起始三角形, end 结束位置
|
||||
// 返回: int 节点位置
|
||||
func (s *MeshSampler) build(first, end int) int {
|
||||
index := len(s.nodes)
|
||||
s.nodes = append(s.nodes, meshSampleNode{})
|
||||
if end-first <= 8 {
|
||||
node := meshSampleNode{minX: math.Inf(1), minY: math.Inf(1), maxX: math.Inf(-1), maxY: math.Inf(-1)}
|
||||
for _, triangle := range s.mesh.Triangles[first:end] {
|
||||
a, b, c := triangle[0].Point, triangle[1].Point, triangle[2].Point
|
||||
d := (b.Y-c.Y)*(a.X-c.X) + (c.X-b.X)*(a.Y-c.Y)
|
||||
if !finite(d) {
|
||||
node = meshSampleNode{minX: math.Inf(-1), minY: math.Inf(-1), maxX: math.Inf(1), maxY: math.Inf(1)}
|
||||
break
|
||||
}
|
||||
for _, vertex := range triangle {
|
||||
node.minX, node.minY = math.Min(node.minX, vertex.Point.X), math.Min(node.minY, vertex.Point.Y)
|
||||
node.maxX, node.maxY = math.Max(node.maxX, vertex.Point.X), math.Max(node.maxY, vertex.Point.Y)
|
||||
}
|
||||
}
|
||||
if finite(node.minX) && finite(node.minY) && finite(node.maxX) && finite(node.maxY) {
|
||||
dx := 2e-12*(node.maxX-node.minX) + 1e-14*math.Max(math.Abs(node.minX), math.Abs(node.maxX))
|
||||
dy := 2e-12*(node.maxY-node.minY) + 1e-14*math.Max(math.Abs(node.minY), math.Abs(node.maxY))
|
||||
node.minX, node.maxX = math.Nextafter(node.minX-dx, math.Inf(-1)), math.Nextafter(node.maxX+dx, math.Inf(1))
|
||||
node.minY, node.maxY = math.Nextafter(node.minY-dy, math.Inf(-1)), math.Nextafter(node.maxY+dy, math.Inf(1))
|
||||
}
|
||||
s.nodes[index] = node
|
||||
return index
|
||||
}
|
||||
middle := first + (end-first)/2
|
||||
left := s.build(first, middle)
|
||||
right := s.build(middle, end)
|
||||
a, b := s.nodes[left], s.nodes[right]
|
||||
s.nodes[index] = meshSampleNode{minX: math.Min(a.minX, b.minX), minY: math.Min(a.minY, b.minY), maxX: math.Max(a.maxX, b.maxX), maxY: math.Max(a.maxY, b.maxY), right: right}
|
||||
return index
|
||||
}
|
||||
|
||||
// sample 优先查询后绘制的区域,包围盒外跳过整组三角形
|
||||
// 入参: index 节点位置, first 起始三角形, end 结束位置, x 横坐标, y 纵坐标
|
||||
// 返回: color.RGBA 采样颜色, bool 是否命中
|
||||
func (s *MeshSampler) sample(index, first, end int, x, y float64) (color.RGBA, bool) {
|
||||
node := s.nodes[index]
|
||||
if x < node.minX || x > node.maxX || y < node.minY || y > node.maxY {
|
||||
return color.RGBA{}, false
|
||||
}
|
||||
if end-first <= 8 {
|
||||
for i := end - 1; i >= first; i-- {
|
||||
if value, ok := meshTriangleColor(&s.mesh.Triangles[i], x, y); ok {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
return color.RGBA{}, false
|
||||
}
|
||||
middle := first + (end-first)/2
|
||||
if value, ok := s.sample(node.right, middle, end, x, y); ok {
|
||||
return value, true
|
||||
}
|
||||
return s.sample(index+1, first, middle, x, y)
|
||||
}
|
||||
Reference in new issue
Block a user