裁剪出碰撞点(manifold) (for GJK的通用改)

EPA得到图形的嵌入方向之后, 就可以裁出碰撞点(边)了.

首先需要找到图形在嵌入方向上最远的一条边.

多边形:

method getFarthestEdgeInDirection*(self: Polygon, direction: Vector2D): Line2D =
    var
        bestIndex: int = 0
        bestProjection: float32 = self[0] * direction
        projection: float32

    #先找出在direction上最远的一个顶点
    for i in 1..self.len-1:
        projection = self[i] * direction

        if projection > bestProjection:
            bestIndex = i
            bestProjection = projection

    #选出与该顶点相邻的两条边, 取在direction上投影最小的那条返回
    let
        left = self[(bestIndex + self.len - 1) mod self.len]
        right = self[(bestIndex + 1) mod self.len]
        mid = self[bestIndex]
        leftProj = abs(left * direction)
        rightProj = abs(right * direction)

    if leftProj > rightProj:
        return newLine2D(left, mid)
    else:
        return newLine2D(mid, right)

圆形只能得到一个点, 不过为了统一还是返回一条长度是0的线段:

method getFarthestEdgeInDirection*(self: Circle, direction: Vector2D): Line2D =
    let p = direction.norm() * self.radius
    return newLine2D(p, p)

还可以为扇形等各种凸图形写一个getFarthestEdgeInDirection函数, 这样就能处理所有的凸图形. 对直线边都返回一条线段, 对弧线边都返回一个点.

于是对所有的凸图形碰撞都分为三种情况, 点与点(两个弧边相碰撞), 点与线段(一个弧形边与一条直线边相碰撞), 线段与线段(两条直线边)

proc getManifold*(self: Contact): Manifold =
    let
        edge1 = self.collisionEdge1
        edge2 = self.collisionEdge2
        isPoint1 = edge1.isPoint()
        isPoint2 = edge2.isPoint()

    if isPoint1:
        if isPoint2:
            result = getPointsManifold(edge1.a, edge2.a)
        else:
            result = getPointLineManifold(edge1.a, edge2)
    else:
        if isPoint2:
            result = getPointLineManifold(edge2.a, edge1)
        else:
            result = getLinesManifold(edge1, edge2, self.penetration)

    result.penetration = self.penetration
    result.rigidA = self.rigidA
    result.rigidB = self.rigidB

 

实际上的刚体碰撞不会陷入到另一个物体内部, 裁剪出来的碰撞点(边)只要在两个图形刚好接触或者小程度的重叠时看上去合理就没问题.

对于点与点的情况, 可以简单的取两点中点:

proc getPointsManifold(p1, p2: Vector2D): Manifold =
    new(result)
    let p = (p1 + p2) * 0.5
    result.add(p)

点与线段的情况:

如果弧上的点夹在线段两点之间, 那么说明是圆形去碰多边形上的一条边, 可以直接返回P点, 也可返回P与线段的中点.

否则, 就是多边形的一个角去碰圆弧, 返回线段上离P最近的那个顶点.

proc getPointLineManifold(point: Vector2D, line: Line2D): Manifold =
    new(result)
    let
        a = line.a
        b = line.b
    if (point - a) * (b - a) < 0: #cos < 0, 夹角大于90°, P在A的外侧
        result.add(line.a)
    elif (point - b) * (a - b) < 0:
        result.add(line.b)
    else:
        result.add(point)

两条线段的情况:

把与嵌入方向更垂直的那一条边找出来当作"参考边", 把另一条当作"事件边".

用参考边去裁剪事件边, 把事件边在参考多边形外侧的部分全部裁掉, 再把事件边在参考边左侧与右侧的部分也裁掉.

借助下面这个函数, 把edge和start都投影在dir上, 把edge投影比start小的部分都裁掉.

proc clipEdge(edge: Manifold, dir: Vector2D, start: Vector2D): Manifold =
    new(result)
    let
        min = start * dir
        proj1 = edge.a * dir - min
        proj2 = edge.b * dir - min
    if proj1 >= 0:
        result.add(edge.a)
    if proj2 >= 0:
        result.add(edge.b)
    if proj1 * proj2 < 0:
        result.add((edge.b - edge.a) * (proj1 / (proj1 - proj2)) + edge.a)
proc getLinesManifold(l1, l2: Line2D, dir: Vector2D): Manifold =
    var
        refVector: Vector2D
        refEdge: Line2D
        incEdge: Manifold
        normal: Vector2D

    let
        v1 = l1.b - l1.a
        v2 = l2.b - l2.a
        proj1 = abs(v1 * dir)
        proj2 = abs(v2 * dir)

    if proj1 < proj2: #投影较小的边更垂直, 作为参考边
        refEdge = l1
        refVector = v1
        incEdge = newManifold(l2)
        normal = tripleProduct(v1, dir.negate(), v1) #得到一个指向(-dir)方向的垂直于v1的向量
    else:
        refEdge = l2
        refVector = v2
        incEdge = newManifold(l1)
        normal = tripleProduct(v2, dir, v2)

    incEdge = clipEdge(incEdge, normal, refEdge.a) #把在参考多边形外部的部分裁剪掉
    if incEdge.length < 2: return incEdge

    normal = tripleProduct(normal, refEdge.b - refEdge.a, normal) #把在a外的部分裁剪掉
    incEdge = clipEdge(incEdge, normal, refEdge.a)
    if incEdge.length < 2: return incEdge

    incEdge = clipEdge(incEdge, normal.negate(), refEdge.b)
    return incEdge

效果如下:

时间: 2024-10-09 05:29:52

裁剪出碰撞点(manifold) (for GJK的通用改)的相关文章

一个不靠谱的2D物理引擎(4) - 裁剪出碰撞点(manifold)

主要参考: http://www.codezealot.org/archives/394 , 建议阅读 第一篇已经解决了如何判断两个图形是否相交以及求嵌入深度. 之后还需要求两物体的接触点. 实际上物体的接触部分可以是一个面, 叫做manifold. 2D下就是一条线段. 对于两个多边形的碰撞, 首先要找出两个多边形相互离得最近的两条边. 在第一篇的修改版的getAxisLeastPenetration函数已经完成了这个工作. 把polyA和polyB相互交换作为该函数的参数执行2次, 取dis

一招教你轻松从图像中裁剪出婚纱礼服和面纱

Super PhotoCut Pro是一款简单粗暴的抠图软件,它能够准确地覆盖你想要去除的任何一个细节区域,抠出图片中你最想要的部分,不需要任何的技巧,那么photocut怎么用呢?你只需要在图片上勾画出需要保留的部分,然后程序会自动处理.从透明物体中删除图像背景是一项复杂而困难的消光任务,即使在您精通Photoshop的前提下也是如此,但现在super photocut将帮助您有效地完成任务, 一分钟抠图不是神话! 使用Super PhotoCut Pro从图像中裁剪出婚纱礼服和面纱教程 1.

裁剪出环形图片

1:首先将一张图片裁剪成圆形图片,, /**圆形图片裁剪*/ - (UIImage *)wjf_circleImage {    //利用self生成一张圆形图片    // 1.开启图形上下文    UIGraphicsBeginImageContextWithOptions(self.size,NO,0);    // 2.描述圆形路径    UIBezierPath*path = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,  

bootstrap删除模态框弹出并询问是否删除【通用删除模态框】

普通的询问是否删除的对话框比较low,可以利用bootstrap的模态框代替普通的对话框来实现删除. 效果: 点删除的时候弹出模态框询问是否删除,点确认的时候将需要删除的ID传到后台进行删除.  过程: 1.界面准备删除模态框: 模态框中隐藏需要删除的ID <!-- 模态框 信息删除确认 --> <div class="modal fade" id="delcfmOverhaul"> <div class="modal-dia

为WebFonts技术实现自动裁剪:帮助汉字从我们的时代幸存下去

第一章 我们应该承认,汉字是不适应网络时代的 只从两点来证明:首先,从来没有过一个完整的汉字字体.英语只包含26个字母,构成一篇文章.一个网页所需的全部字符都包含在ASCII码表内,每个字符在计算机中只需要一个字节表示.而反观汉字,甚至没有定论究竟有多少个字.目前我们可以使用的汉字(包括日语.韩语中的)大约有8万个,Unicode中定义了7万多个,在中国大陆地区广泛使用的GBK编码收录了2万个,GB2312编码收录了6千多个,在香港.台湾广泛使用的BIG5编码收录了1.3万个.一般情况下这些编码

Android 相册图片选取+自定义裁剪方式(非系统裁剪)

不多说,直接上代码(裁剪的代码摘自网络.)(项目可运行) 主要是系统自身的剪切方式在有些机型上会程序崩溃的问题. 1 package com.jichun.activity; 2 3 import java.io.FileNotFoundException; 4 5 import com.jichun.view.CropCanvas; 6 7 import android.app.Activity; 8 import android.content.ContentResolver; 9 impo

WP8.1开发对图片进行裁剪(截取)一部分

对于这个内容,我是在学习插入图片时遇到的问题,在Windows应用中可以直接用Image类和Bitmap直接实现裁剪功能, 在wp上就不行了,后来在MSDN论坛上找到了相关方法及示例,而示例太复杂了, 我就参考https://social.msdn.microsoft.com/Forums/zh-CN/07b06a88-607b-44d8-ae5c-801d5c8b3a27上的简单示例, 因为示例没有太多注解,我在配合自己开发的程序上进行了注解和自己的理解,方便大家: 采用了WriteableB

Canvas裁剪Clip和Region、RegionIterator

extends:http://blog.csdn.net/lonelyroamer/article/details/8349601 裁剪功能由Canvas提供的一系列的clip...方法 和quickReject方法来完成. 前面已经提到,真正提供可绘制区域的是Canvas内部的mutable bitmap. Canvas更像是一个图层,我们只能在这上面的图层来绘制东西. 1.首先介绍Region类 Region,中文意思即区域的意思,它表示的是canvas图层上的某一块封闭的区域. /**构造

Android 2D Graphics学习 Region和Canvas裁剪

1.首先介绍Region类 Region,中文意思即区域的意思,它表示的是canvas图层上的某一块封闭的区域. [java] view plaincopyprint? /**构造方法*/ public Region()  //创建一个空的区域 public Region(Region region) //拷贝一个region的范围 public Region(Rect r)  //创建一个矩形的区域 public Region(int left, int top, int right, int