• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Snippet] Triangle

JASS:
library Triangle /* v1.0
************************************************************************************
*
*    Allows you to create triangles.
*
************************************************************************************
*
*    */ uses /* 
*
*    */ Alloc /* 
*     - hiveworkshop.com/forums/jass-resources-412/snippet-alloc-alternative-221493/
*
************************************************************************************
*
*    API
*
*    static method create takes real ax, real ay, real bx, real by, real cx, real cy returns thistype
*        - create a Triangle
*
*    method assign takes thistype t returns nothing
*        - assigns the properties of "t" to the triangle.
*
*    method copy takes nothing returns thistype
*        - copies a triangle by creating a new triangle and assigning it's properties to it.
*
*    method scale takes real scale returns nothing
*        - scale the Triangle size
*
*    method rotate takes real radians returns nothing
*        - rotate the Triangle
*
*    method moveCentroid takes real x, real y returns nothing
*        - move the Triangle by it's centroid
*
*    method containsPoint takes real x, real y returns boolean
*        - check if the Triangle contains a point.
*
*    method getCentroidX takes nothing returns real
*        - get the x centroid of the Triangle
*
*    method getCentroidY takes nothing returns real
*        - get the y centroid of the Triangle
*
*    method setVertexX takes integer v, real x returns nothing
*    method setVertexY takes integer v, real y returns nothing
*        - set the coordinates of a given vertex of a Triangle.
*
*    method getVertexX takes integer v returns real
*    method getVertexY takes integer v returns real
*        - get the coordinate value of a given vertex of the Triangle
*
*    method collides takes thistype t returns boolean
*        - checks if the Triangle collides with the other Triangle.
*
*    method destroy takes nothing returns nothing
*        - destroys/removes a Triangle
*
************************************************************************************
*
*    Credits
*
*    Nestharus - Alloc
*    lfh - intersection formula
*
************************************************************************************/
    private function GetDistance takes real x1, real x2, real y1, real y2 returns real
        return SquareRoot((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1))
    endfunction

    struct Triangle extends array
    	readonly static constant integer A = 0
    	readonly static constant integer B = 1
    	readonly static constant integer C = 2
        
        private real t_ax
        private real t_ay
        private real t_bx
        private real t_by
        private real t_cx
        private real t_cy
        private real t_ctx
        private real t_cty
        
        private real t_actangle
        private real t_bctangle
        private real t_cctangle
        private real t_actdist
        private real t_bctdist
        private real t_cctdist

        implement Alloc

        static method create takes real ax, real ay, real bx, real by, real cx, real cy returns thistype
            local thistype this = allocate()
            set t_ax = ax
            set t_ay = ay
            set t_bx = bx
            set t_by = by
            set t_cx = cx
            set t_cy = cy
            set t_ctx = (ax + bx + cx)/3
            set t_cty = (ay + by + cy)/3
            set t_actangle = Atan2(ay - t_cty, ax - t_ctx)
            set t_bctangle = Atan2(by - t_cty, bx - t_ctx)
            set t_cctangle = Atan2(cy - t_cty, bx - t_ctx)
            set t_actdist = GetDistance(ax, t_ctx, ay, t_cty)
            set t_bctdist = GetDistance(bx, t_ctx, by, t_cty)
            set t_cctdist = GetDistance(cx, t_ctx, cy, t_cty)
            return this
        endmethod

        method assign takes thistype t returns nothing
            set t_ax = t.t_ax
            set t_ay = t.t_ay
            set t_bx = t.t_bx
            set t_by = t.t_by
            set t_cx = t.t_cx
            set t_cy = t.t_cy
            set t_ctx = t.t_ctx
            set t_cty = t.t_cty
            set t_actangle = t.t_actangle
            set t_bctangle = t.t_bctangle
            set t_cctangle = t.t_cctangle
            set t_actdist = t.t_actdist
            set t_bctdist = t.t_bctdist
            set t_cctdist = t.t_cctdist
        endmethod
        
        method copy takes nothing returns thistype
            local thistype t = create(0, 0, 0, 0, 0, 0)
            call t.assign(this)
            return t
        endmethod
        
        private method update takes nothing returns nothing
            set t_ax = t_ctx + t_actdist * Cos(t_actangle)
            set t_ay = t_cty + t_actdist * Sin(t_actangle)
            set t_bx = t_ctx + t_bctdist * Cos(t_bctangle)
            set t_by = t_cty + t_bctdist * Sin(t_bctangle)
            set t_cx = t_ctx + t_cctdist * Cos(t_cctangle)
            set t_cy = t_cty + t_cctdist * Sin(t_cctangle)
        endmethod

        method scale takes real scale returns nothing
            set t_actdist = t_actdist * scale
            set t_bctdist = t_bctdist * scale
            set t_cctdist = t_cctdist * scale
            call update()
        endmethod

        method rotate takes real radians returns nothing
            set t_actangle = t_actangle + radians
            set t_bctangle = t_bctangle + radians
            set t_cctangle = t_cctangle + radians
            call update()
        endmethod

        method moveCentroid takes real x, real y returns nothing
            set t_ctx = x
            set t_cty = y
            call update()
        endmethod

        method containsPoint takes real x, real y returns boolean
            local real cross0 = (y - t_ay)*(t_bx - t_ax) - (x - t_ax)*(t_by - t_ay)
            local real cross1 = (y - t_cy)*(t_ax - t_cx) - (x - t_cx)*(t_ay - t_cy)
            return cross0*cross1 >= 0 and ((y - t_by)*(t_cx - t_bx) - (x - t_bx) * (t_cy - t_by)) * cross1 >= 0
        endmethod

        method getCentroidX takes nothing returns real
            return t_ctx
        endmethod

        method getCentroidY takes nothing returns real
            return t_cty
        endmethod

        private method updateCent takes nothing returns nothing
            set t_ctx = (t_ax + t_bx + t_cx)/3
            set t_cty = (t_ay + t_by + t_cy)/3
        endmethod
        
        method setVertexX takes integer v, real x returns nothing
            if v == A then
                set t_ax = x
                call updateCent()
                set t_actangle = Atan2(t_ay - t_cty, t_ax - t_ctx)
                set t_actdist = GetDistance(t_ax, t_ay, t_ctx, t_cty)
            elseif v == B then
                set t_bx = x
                call updateCent()
                set t_bctangle = Atan2(t_by - t_cty, t_bx - t_ctx)
                set t_bctdist = GetDistance(t_bx, t_by, t_ctx, t_cty)
            elseif v == C then
                set t_cx = x
                call updateCent()
                set t_cctangle = Atan2(t_cy - t_cty, t_cx - t_ctx)
                set t_cctdist = GetDistance(t_cx, t_cy, t_ctx, t_cty)
            endif
        endmethod

        method setVertexY takes integer v, real y returns nothing
            if v == A then
                set t_ay = y
                call updateCent()
                set t_actangle = Atan2(t_ay - t_cty, t_ax - t_ctx)
                set t_actdist = GetDistance(t_ax, t_ay, t_ctx, t_cty)
            elseif v == B then
                set t_by = y
                call updateCent()
                set t_bctangle = Atan2(t_by - t_cty, t_bx - t_ctx)
                set t_bctdist = GetDistance(t_bx, t_by, t_ctx, t_cty)
            elseif v == C then
                set t_cy = y
                call updateCent()
                set t_cctangle = Atan2(t_cy - t_cty, t_cx - t_ctx)
                set t_cctdist = GetDistance(t_cx, t_cy, t_ctx, t_cty)
            endif
        endmethod

        method getVertexX takes integer v returns real
            if v == A then
                return t_ax
            elseif v == B then
                return t_bx
            elseif v == C then
                return t_cx
            endif
            return 0.
        endmethod

        method getVertexY takes integer v returns real
            if v == A then
                return t_ay
            elseif v == B then
                return t_by
            elseif v == C then
                return t_cy
            endif
            return 0.
        endmethod

        private method doesIntersect takes real x1, real y1, real x2, real y2, real x3, real y3, real x4, real y4, thistype t returns boolean
            local real a1 = (y2 - y1)/(x2 - x1)
            local real a2 = (y4 - y3)/(x4 - x3)
            local real b1 = y1 - a1*x1
            local real b2 = y3 - a2*x3
            local real x = (b2 - b1)/(a1 - a2)
            local real y_1 = a1*x + b1
            local real y_2 = a2*x + b2

            if RAbsBJ(y_1 - y_2) > 0.01 then
                return false
            endif
            return (containsPoint(x, y_1) and t.containsPoint(x, y_1))
        endmethod
        
        method collides takes thistype t returns boolean
            local boolean b1 = doesIntersect(t_ax, t_ay, t_bx, t_by, t.t_ax, t.t_ay, t.t_bx, t.t_by, t)
            local boolean b2 = doesIntersect(t_ax, t_ay, t_bx, t_by, t.t_ax, t.t_ay, t.t_cx, t.t_cy, t)
            local boolean b3 = doesIntersect(t_ax, t_ay, t_bx, t_by, t.t_bx, t.t_by, t.t_cx, t.t_cy, t)
            local boolean b4 = doesIntersect(t_ax, t_ay, t_cx, t_cy, t.t_ax, t.t_ay, t.t_bx, t.t_by, t)
            local boolean b5 = doesIntersect(t_ax, t_ay, t_cx, t_cy, t.t_ax, t.t_ay, t.t_cx, t.t_cy, t)
            local boolean b6 = doesIntersect(t_ax, t_ay, t_cx, t_cy, t.t_bx, t.t_by, t.t_cx, t.t_cy, t)
            local boolean b7 = doesIntersect(t_bx, t_by, t_cx, t_cy, t.t_ax, t.t_ay, t.t_bx, t.t_by, t)
            local boolean b8 = doesIntersect(t_bx, t_by, t_cx, t_cy, t.t_ax, t.t_ay, t.t_cx, t.t_cy, t)
            local boolean b9 = doesIntersect(t_bx, t_by, t_cx, t_cy, t.t_bx, t.t_by, t.t_cx, t.t_cy, t)
            return b1 or b2 or b3 or b4 or b5 or b6 or b7 or b8 or b9
        endmethod

        method destroy takes nothing returns nothing
            call deallocate()
            set t_ax = 0
            set t_ay = 0
            set t_bx = 0
            set t_by = 0
            set t_cx = 0
            set t_cy = 0
            set t_actangle = 0
            set t_bctangle = 0
            set t_cctangle = 0
            set t_actdist = 0
            set t_bctdist = 0
            set t_cctdist = 0
            set t_ctx = 0
            set t_cty = 0
        endmethod
    endstruct
endlibrary

Demo:
JASS:
struct Tester extends array
    private static method onInit takes nothing returns nothing
        // Create a triangle
        local Triangle t = Triangle.create(0, 0, 10, 0, 0, 10)
        
        // scale it
        call t.scale(2)
        
        // rotate it
        call t.rotate(bj_PI)
        
        // move it
        call t.moveCentroid(0, 0)
        
        // check if it contains point
        if t.containsPoint(1, 1) then
            call BJDebugMsg("Triangle contains (1, 1)")
        endif
        
        // get its coordinates
        call BJDebugMsg("( " + R2S(t.getCentroidX()) + " , " + R2S(t.getCentroidY()) + " ) ")
        
        // check if it collides with a triangle
        if t.collides(Triangle.create(0, 0, 1, 0, 0, 1)) then
            call BJDebugMsg("a triangle collided with another triangle")
        endif
        
        // destroy it
        call t.destroy()
    endmethod
endstruct
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
Updated.
Now 100% complete.

Not sure though with the formula of line intersection XD

JASS:
private function DoesIntersect takes real x1, real y1, real x2, real y2, real x3, real y3, real x4, real y4 returns boolean
    local real a1 = (y2 - y1)/(x2 - x1)
    local real a2 = (y4 - y3)/(x4 - x3)
    local real b1 = y1 - a1*x1
    local real b2 = y3 - a2*x3
    local real m = (b2 - b1)/(a2 - a1)
    local real y = b1 - a1*m
    local real x = -m/y
    return a1*x + b1 == a2*x + b2
endfunction

->

JASS:
globals
    private constant real EPSILON = 0.01
endglobals

/*
.
.
.
*/

// Todo: Remove redundant arguments 
private method doesIntersect takes real x1, real y1, real x2, real y2, real x3, real y3, real x4, real y4, thistype t returns boolean
    local real a1 = (y2 - y1)/(x2 - x1)
    local real a2 = (y4 - y3)/(x4 - x3)
    local real b1 = y1 - a1*x1
    local real b2 = y3 - a2*x3
    local real x = (b2 - b1)/(a1 - a2)
    local real y_1 = a1*x + b1
    local real y_2 = a2*x + b2

    if RAbsBJ(y_1 - y_2) > EPSILON then
        return false
    endif
    return(containsPoint(x, y_1) and t.containsPoint(x, y_1))
endmethod

And

JASS:
method collides takes thistype t returns boolean
    local boolean r1 = DoesIntersect(t_ax, t_ay, t_bx, t_by, t.t_ax, t.t_ay, t.t_bx, t.t_by)
    local boolean r2 = DoesIntersect(t_bx, t_by, t_cx, t_cy, t.t_bx, t.t_by, t.t_cx, t.t_cy)
    local boolean r3 = DoesIntersect(t_cx, t_cy, t_ax, t_ay, t.t_cx, t.t_cy, t.t_ax, t.t_ay)
    local boolean r4 = containsPoint(t.getVertexX(Vertex.A), t.getVertexY(Vertex.A))
    local boolean r5 = containsPoint(t.getVertexX(Vertex.B), t.getVertexY(Vertex.B))
    local boolean r6 = containsPoint(t.getVertexX(Vertex.C), t.getVertexY(Vertex.C))
    return r1 or r2 or r3 or r4 or r5 or r6
endmethod

->

JASS:
method collides takes thistype t returns boolean
    local boolean b1 = doesIntersect(t_ax, t_ay, t_bx, t_by, t.t_ax, t.t_ay, t.t_bx, t.t_by, t)
    local boolean b2 = doesIntersect(t_ax, t_ay, t_bx, t_by, t.t_ax, t.t_ay, t.t_cx, t.t_cy, t)
    local boolean b3 = doesIntersect(t_ax, t_ay, t_bx, t_by, t.t_bx, t.t_by, t.t_cx, t.t_cy, t)
    local boolean b4 = doesIntersect(t_ax, t_ay, t_cx, t_cy, t.t_ax, t.t_ay, t.t_bx, t.t_by, t)
    local boolean b5 = doesIntersect(t_ax, t_ay, t_cx, t_cy, t.t_ax, t.t_ay, t.t_cx, t.t_cy, t)
    local boolean b6 = doesIntersect(t_ax, t_ay, t_cx, t_cy, t.t_bx, t.t_by, t.t_cx, t.t_cy, t)
    local boolean b7 = doesIntersect(t_bx, t_by, t_cx, t_cy, t.t_ax, t.t_ay, t.t_bx, t.t_by, t)
    local boolean b8 = doesIntersect(t_bx, t_by, t_cx, t_cy, t.t_ax, t.t_ay, t.t_cx, t.t_cy, t)
    local boolean b9 = doesIntersect(t_bx, t_by, t_cx, t_cy, t.t_bx, t.t_by, t.t_cx, t.t_cy, t)
    return b1 or b2 or b3 or b4 or b5 or b6 or b7 or b8 or b9
endmethod



EDIT:

Just to mention it, this is no final solution but just correcting the errors in the functions.

This will still fail if one Triangle side is parallel to the y-Axis. You would either have to perform some coordinate
transformation (bad) or choose a different representation (good) of your Triangles to solve this.

Also the performance can be improved a lot if you first check whether the two Triangle circumcircles
intersect or not (if not, the Triangles can't intersect neither).

The containsPoint method should also work with a tolerance to overcome Wc3 real inaccuracy. You might also change the doesIntersect method so that it only takes thistype t. I just put it in there quickly, but as you have to pass t to it anyways it doesn't make much sense to pass so many arguments to it.

And what is this for?

JASS:
static method operator Vertex takes nothing returns Vertices
    return 0
endmethod

EDIT 2:

Your containsPoint method is also not working correctly ;)

This displays "contains Point" although the point is obviously not in the Triangle (not an accuracy problem).

JASS:
struct Tester extends array
    private static method onInit takes nothing returns nothing
        local Triangle t = Triangle.create(3, 3, 6, 2, 5, 6)
        
        if t.containsPoint(2, 1.5) then
            call BJDebugMsg("contains Point")
        else
            call BJDebugMsg("NOT contains Point")
        endif
    endmethod
endstruct
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
Hm,

well you updated it but you didn't solve any of the problems I pointed out... :D

- containsPoint still doesn't work
- doesIntersect still has redundant arguments
- performance is still bad due to not checking the triangles circumcircles
- it still fails for triangles that have one side parallel to the y-axis; even your own example crashes the thread due to that^^


Also you should make the accuracy configurable in a globals block as I showed you. No magic numbers in the code.

There is still a lot to fix here...
 
Last edited:
Level 22
Joined
Sep 24, 2005
Messages
4,821
I was wondering if the copy method could be written like so:
JASS:
        method copy takes nothing returns thistype
            local thistype t = allocate()
            call t.assign(this)
            return t
        endmethod

So as to remove the unnecessary overhead of the create method.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
There are different schools of thought TriggerHappy ; ). Some aim for minimal requirements, going for many optionals or no requirements at all. Others aim for maximal requirements, abstracting as much as possible out to other resources.


It's not your place to say which is the better approach ^)^. They both have their merits. I'm personally for the abstraction approach, but that doesn't mean that I go to threads telling people that have no requirements when they could have some that they need to depend on other resources instead of write the code themselves =).

Just my two cents. No reason for him to not use Alloc. The Alloc algorithm changes every once and awhile, and the feature set also changes. The API does not. This actually gives him the advantage of using the most modern/best algorithm possible w/o having to update his thing ^_^.


I don't think you should try to enforce one school of thought.

As for a solution, I would think that rating a resource based on quality (not usefulness, solely quality) in the thread and then approving it simply if it works and is not blatantly stealing from someone else could be good. Would need a better way to do resource categorization and searching though, but meh.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
lol, I gave a few very valid reasons.

I would personally say to not give into TriggerHappy's demands. Those demands are subjective and based on preference ; ), thus they do not need to be followed : P.


There is absolutely nothing against library requirements in rules and library requirements also promote good programming. TriggerHappy's demand is unfounded and he has no right to not approve a resource for this reason ^)^. Doing so would be an abuse of power. It is up to the author's preference as to whether or not they want to make Alloc optional, required, or not used at all. All are valid choices. A moderator has no say in this manner unless Alloc degrades the quality of the resource, which it does not ; ).

Almia could easily take this up to higher authorities.


I would say to not give into this at all to keep THW a free place, that is, unless it can be proven that Alloc degrades the quality of the resource. Until then, it is a blatant abuse of power.

I am simply trying to protect the rights of the users here. That, and such a statement to me is totally unacceptable ;). This is prejudice.
 
I'm not approving resources with Alloc. I already stated it's an more than "unnecessary requirement".

Alloc (in my opinion) is the equivalent of creating a pseudo function to replace a BJ function for a very slight speed increase. I wouldn't approve a resource with unnecessary pseudo functions either.

It's not like he can't make it optional (which produces ugly code, but just don't use alloc for public resources ;) ).

There are other people capable of approving JASS resources.

If you have a problem with it maybe another moderator has a different opinion and he can review your script.

TriggerHappy's demand is unfounded and he has no right to not approve a resource for this reason

Almia could easily take this up to higher authorities.

Doing so would be an abuse of power.

protect the rights of the users here.

Get over yourself, lmao. How dramatic can you be?

and why are you acting like were congress or something? It's a modding site for a dying game on a section where nobody submits anything.

Sure go ahead report me if it makes you feel better. You're not going to force me to do anything. It's obvious your upset because I'm not approving a resource which requires one of yours.

Don't expect any more responses to you. If the author as a problem he can contact me (or someone else) and it can get sorted out, so mind your own business please.

Note: I'm pretty sure most JASS people share my opinion.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
#1: indeed there is a conflict of interest, but if you think it has any bearing on this, you don't know me. However, due to a conflict of inerest, i will back off here assuming that someone else will take over.

#2: if you think that Alloc is nothing but a slight speed boost, you're wrong again. It provides debugging functionality.

#3: i disagree with you on approval. As a moderator, you *have* to approve something if it is objectively worthy of approval, regardless of whether or not you like it or agree with it. It is your obligation, responsibility, and job. I know that you are a volunteer. As a volunter, you are free to choose what resources you are willing to review. It is more succint for you to simply not review this resource and leave it for someone else. What you are doing instead is out of line.

If someone asks you why you won't review it, you can say that it goes against your ideals or something, and as a volunteer, this is well within your rights.

It is sometimes better to leave some things left unsaid :).

That is all i have to say on the matter.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
TH, what's wrong with requiring other libraries? Perhaps independency is your taste, where something that requires less thing is neater and better. But, remember: no one is obliged to follow other people's coding style. No one is obliged to follow your neat style. If he/she likes his/her way, then you should understand, as long as they are not in a wrong way.

I understand some of Nesth's statements above. And agreed with some of them. You can't make your own rule, set a higher standart is good, but just follow the big line. If they are good, just let them in.

On the other side, I understand that maybe TH got some reasons why Alloc probably is not a valid requirement. I also don't understand why this library have to use Alloc though. Probably set it as optional will be better. So people who demands speed more than less-requirements may choose.

I'm not siding anybody here, just sharing my thoughts.
 
Me said:
Alloc (in my opinion) is the equivalent of creating a pseudo function to replace a BJ function for a very slight speed increase. I wouldn't approve a resource with unnecessary pseudo functions

There's my reason. I have nothing else to say about it.

and I'm not against requiring libraries, that's ridiculous.
 
It is okay to use alloc. A moderator/user has every right to speak their mind on the topic, but the use of alloc won't qualify for rejection unless we write it out in the rules (considering many past things were approved that use alloc).

However, it does need an updated link to a valid version of alloc alternative. Otherwise we can't approve it.
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
Triangles should be also supported by [vJASS] - [System] Polygon.

How ever it is currently not approved, and it needs to be tested.

But Triangles are supported by my Polygon System, in fact in supports any non-self intersecting shape there is.
By the way, Polygon System has a very minor scripting error, I forgot to place "requires optional Table".
 

Submission:
Triangle v1.0

Date:
2 November 16

Status:
Graveyard
Note:
http://www.hiveworkshop.com/threads/system-polygon.276031/

Polygon does cover triangles, and has a wider API. We probably do not need more
submissions to check for specific polygons from now on. Sharing algorithms might
probably still becool in our Code Snippets thread, but at the moment not in JASS submissions. Graveyard.
 
Top