HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

2D Simple Shape System (2D3S) oppinions needed

03-11-2009, 10:07 PM#1
Flame_Phoenix
Ok guys, again recently I searched the database for a recent shape system, but I only found systems using Kattana's ... which is obviously bad and outdated, so I decided to start making my own shape system. So far the system is quite basic, and is very easy to read once you understand structure Line and Circle.
I hope people have suggestions, I intend to add more geometric formulas, and after that submit it to the community.

My spell for contest 14 uses an Alpha version of this system. The current version I present here is more advanced. Hope people like it =)

Collapse JASS:
//===========================================================================
//A system that allows the user to create many shapes in a very simple and 
//customizable way.
//
//Requires TimerUtils
//
//@author Flame_Phoenix
//
//@credits
//- ThredNThrash, for the main idea and math formulas
//
//@version 1.0
//===========================================================================
library SimpleShapes requires TimerUtils
    struct Circle
        group effs
        timer t
        real x1
        real y1
        real centerX
        real centerY
        real unitSize
        integer unitId
        real radius
        player owner
        integer start
        integer end 
        
        static method createEff takes nothing returns nothing
            //we get the structure from the timer
            local Circle data = Circle(GetTimerData(GetExpiredTimer())) 
        
            if data.start <= data.end then
                //polar projection stuff
                set data.x1 = data.centerX + data.radius * Cos(data.start * ( 360.00 / (data.radius / data.unitSize )) * bj_DEGTORAD) 
                set data.y1 = data.centerY + data.radius * Sin(data.start * ( 360.00 / (data.radius / data.unitSize )) * bj_DEGTORAD) 
                
                call GroupAddUnit(data.effs,  CreateUnit(data.owner, data.unitId, data.x1, data.y1, 0))
                set data.start = data.start + 1
            else
                call PauseTimer(data.t)
            endif
        endmethod
        
        static method create takes real x, real y, real radius, integer unitId, real unitSize, player owner, real delay returns Circle
            local Circle data = Circle.allocate()
            
            //recycling the group is always good
            if data.effs == null then
                set data.effs = CreateGroup()
            endif
            
            set data.owner = owner
            set data.centerX = x
            set data.centerY = y
            set data.start = 1
            set data.radius = radius
            set data.unitSize = unitSize
            set data.unitId = unitId
            set data.end = R2I(radius / unitSize)
            
            set data.t = NewTimer()
            
            //we attach the struct to the timer
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, delay, true, function Circle.createEff)

            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            local unit f
            loop
                set f = FirstOfGroup(.effs)
                exitwhen(f == null)
                call GroupRemoveUnit(.effs, f)
                call KillUnit(f)
            endloop
            call GroupClear(.effs)
            
            call ReleaseTimer(.t)
        endmethod
    endstruct
    
    struct Line
        real startX
        real startY
        real dist
        real x1
        real y1
        real angle
        integer start
        integer unitsNum    //the maximum amount of units we create
        group effs
        timer t
        player owner
        integer unitId
        
        static method createEffL takes nothing returns nothing
            //we get the structure from the timer
            local Line data = Line(GetTimerData(GetExpiredTimer())) 

            if data.start <= data.unitsNum then
                //polar projection stuff
                set data.x1 = data.startX + ((data.start * data.dist) / data.unitsNum) * Cos(data.angle)
                set data.y1 = data.startY + ((data.start * data.dist) / data.unitsNum) * Sin(data.angle)

                call GroupAddUnit(data.effs, CreateUnit(data.owner, data.unitId, data.x1, data.y1, 0))
                set data.start = data.start + 1
            else
                call PauseTimer(data.t)
            endif
        endmethod
        
        static method create takes real sX, real sY, real eX, real eY, integer unitId, real unitSize, player owner, real delay returns Line
            local Line data = Line.allocate()
            local real dx
            local real dy
            
            //setting members and group
            set data.owner = owner
            set data.unitId = unitId
            
            //points, lenght, angle and distance
            set data.startX = sX 
            set data.startY = sY
            set dx = sX - eX
            set dy = sY - eY
            set data.angle = Atan2(eY - sY, eX - sX)
            set data.dist = SquareRoot(dx*dx + dy*dy)
            
            //number of units needed
            set data.unitsNum = R2I(data.dist / (unitSize * 5) )
            
            set data.start = 1  //starting our counter
            
            //recycling the group =P
            if data.effs == null then
                set data.effs = CreateGroup()
            endif
            
            set data.t = NewTimer()
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, delay, true, function Line.createEffL)
            
            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            local unit f
            loop
                set f = FirstOfGroup(.effs)
                exitwhen(f == null)
                call GroupRemoveUnit(.effs, f)
                call KillUnit(f)
            endloop
            call GroupClear(.effs)
            
            call ReleaseTimer(.t)
        endmethod
    endstruct
    
    struct Pentagram
        Line l1
        Line l2
        Line l3
        Line l4
        Line l5
        
        static method create takes real x, real y, real radius, integer unitId, real unitSize, player owner, real delay returns Pentagram
            local Pentagram data = Pentagram.allocate()
            local real x1
            local real y1
            local real x2
            local real y2
            
            //all this stuff creates the first line of the start ...
            set x1 = x + radius * Cos(234.0 * bj_DEGTORAD)
            set y1 = y + radius * Sin(234.0 * bj_DEGTORAD)
            set x2 = x + radius * Cos(90. * bj_DEGTORAD)
            set y2 = y + radius * Sin(90. * bj_DEGTORAD)

            set data.l1 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            //now prepare for the second line of the start lol... don't worry, it 
            //only has 5 lines xD
            set x1 = x + radius * Cos(90. * bj_DEGTORAD)
            set y1 = y + radius * Sin(90. * bj_DEGTORAD)
            set x2 = x + radius * Cos(306. * bj_DEGTORAD)
            set y2 = y + radius * Sin(306. * bj_DEGTORAD)
            
            set data.l2 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)

            //now for the 3rd line
            set x1 = x + radius * Cos(306. * bj_DEGTORAD)
            set y1 = y + radius * Sin(306. * bj_DEGTORAD)
            set x2 = x + radius * Cos(162. * bj_DEGTORAD)
            set y2 = y + radius * Sin(162. * bj_DEGTORAD)
            
            set data.l3 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            //now for the 4th line, don't worry, we are almost there
            set x1 = x + radius * Cos(162. * bj_DEGTORAD)
            set y1 = y + radius * Sin(162. * bj_DEGTORAD)
            set x2 = x + radius * Cos(19. * bj_DEGTORAD)
            set y2 = y + radius * Sin(19. * bj_DEGTORAD)
            
            set data.l4 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            //at last the last line of our David star!
            set x1 = x + radius * Cos(19.0 * bj_DEGTORAD)
            set y1 = y + radius * Sin(19.0 * bj_DEGTORAD)
            set x2 = x + radius * Cos(234. * bj_DEGTORAD)
            set y2 = y + radius * Sin(234. * bj_DEGTORAD)

            set data.l5 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)

            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            call .l1.destroy()
            call .l2.destroy()
            call .l3.destroy()
            call .l4.destroy()
            call .l5.destroy()
        endmethod
    endstruct
    
    struct Xspot 
        Line l1
        Line l2
        
        static method createDefault takes real origX, real origY, real length, integer unitId, real unitSize, player owner, real delay returns Xspot
            local Xspot data = Xspot.allocate()
            
            local real x1 = origX + length * Cos(315. * bj_DEGTORAD)
            local real y1 = origY + length * Sin(315. * bj_DEGTORAD)
            local real x2 = origX + length * Cos(135. * bj_DEGTORAD)
            local real y2 = origY + length * Sin(135. * bj_DEGTORAD)
            
            set data.l1 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            set x1 = origX + length * Cos(225. * bj_DEGTORAD)
            set y1 = origY + length * Sin(225. * bj_DEGTORAD)
            set x2 = origX + length * Cos(45. * bj_DEGTORAD)
            set y2 = origY + length * Sin(45. * bj_DEGTORAD)
            
            set data.l2 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            return data
        endmethod
        
        static method createCustom takes real origX, real origY, real length, real angle1, real angle2, real angle3, real angle4, integer unitId, real unitSize, player owner, real delay returns Xspot
            local Xspot data = Xspot.allocate()
            
            local real x1 = origX + length * Cos(angle1 * bj_DEGTORAD)
            local real y1 = origY + length * Sin(angle1 * bj_DEGTORAD)
            local real x2 = origX + length * Cos(angle2 * bj_DEGTORAD)
            local real y2 = origY + length * Sin(angle2 * bj_DEGTORAD)
            
            set data.l1 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            set x1 = origX + length * Cos(angle3 * bj_DEGTORAD)
            set y1 = origY + length * Sin(angle3 * bj_DEGTORAD)
            set x2 = origX + length * Cos(angle4 * bj_DEGTORAD)
            set y2 = origY + length * Sin(angle4 * bj_DEGTORAD)
            
            set data.l2 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            call .l1.destroy()
            call .l2.destroy()
        endmethod
    endstruct
    
    struct Square
        Line l1
        Line l2
        Line l3
        Line l4
        
        static method create takes real origX, real origY, real radius, integer unitId, real unitSize, player owner, real delay returns Square
            local Square data = Square.allocate()
            
            local real x1 = origX + radius * Cos(225. * bj_DEGTORAD)
            local real y1 = origY + radius * Sin(225. * bj_DEGTORAD)
            local real x2 = origX + radius * Cos(315. * bj_DEGTORAD)
            local real y2 = origY + radius * Sin(315. * bj_DEGTORAD)
            
            set data.l1 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            set x1 = origX + radius * Cos(315. * bj_DEGTORAD)
            set y1 = origY + radius * Sin(315. * bj_DEGTORAD)
            set x2 = origX + radius * Cos(45. * bj_DEGTORAD)
            set y2 = origY + radius * Sin(45. * bj_DEGTORAD)
            
            set data.l2 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            set x1 = origX + radius * Cos(45. * bj_DEGTORAD)
            set y1 = origY + radius * Sin(45. * bj_DEGTORAD)
            set x2 = origX + radius * Cos(135. * bj_DEGTORAD)
            set y2 = origY + radius * Sin(135. * bj_DEGTORAD)
            
            set data.l3 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            set x1 = origX + radius * Cos(135. * bj_DEGTORAD)
            set y1 = origY + radius * Sin(135. * bj_DEGTORAD)
            set x2 = origX + radius * Cos(225. * bj_DEGTORAD)
            set y2 = origY + radius * Sin(225. * bj_DEGTORAD)
            
            set data.l4 = Line.create(x1, y1, x2, y2, unitId, unitSize, owner, delay)
            
            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            call .l1.destroy()
            call .l2.destroy()
            call .l3.destroy()
            call .l4.destroy()
        endmethod
    endstruct
endlibrary

The system is really easy to use, I hope people like it.
Here is a test Demo map with samples.
Attached Files
File type: w3x2D3S.w3x (28.8 KB)
03-11-2009, 11:10 PM#2
xombie
How exactly do you use this?

I don't mean, how do you call the constructors/destructors, I mean what does it do with these geometric shapes? Upon further reading it seems like it creates units (and adds them to a group) in geometric patterns, is this correct?
03-11-2009, 11:12 PM#3
Blacktastic
Geometric Midnight Theatre anyone? I'll bring the rulers.

Seriously though, even with spell application..hmm.. maybe you should add in a guide on what it can be used FOR rather than HOW :p.
03-11-2009, 11:13 PM#4
xombie
Blacktastic you will never beat me to the punch. Stop trying.
03-11-2009, 11:15 PM#5
Blacktastic
Gotta have something to occupy my time unitl someone gets back to me about my item issue because for once I am completely dumbfounded as to how something so simple isnt functioning.

Anyway, back on topic, I will say this is pretty cool regardless.
03-11-2009, 11:25 PM#6
akolyt0r
you do LOTS of calculations in the timer callbacks which can be before ...and you should stop using degrees ....use radians ...
03-12-2009, 06:06 AM#7
Pyrogasm
I would make this system allow for user input in the following way:
  • There is a basic struct called a "shape" that has a few basic methods:
    • Create
    • Destroy
    • Move
    • Rotate
  • Each complex shape (line, circle, cross, square, etc.) is then an extension of a shape (struct Cross extends Shape), which would have its own methods to overwrite .destroy(), .move(), .create(), .rotate(), etc., or even have new specific methods of its own.
Then, in your system you'd provide a few of the basic ones in the main system (Line, circle), and then have a separate library that would house all the 'complex' shapes. You could add the ones you have now, and then allow the user to create his or her own new shapes and add them to the second library.
03-12-2009, 11:53 AM#8
xombie
Rather than making it a struct you could probably just make it an interface.
03-12-2009, 01:16 PM#9
Flame_Phoenix
Quote:
How exactly do you use this?
Quote:
Seriously though, even with spell application..hmm.. maybe you should add in a guide on what it can be used FOR rather than HOW :p.
Ok, there are many applications for this system. Per exemple, my entry for the contest uses this system (an alpha version of it). You can enter the code and see how easy it is to transform a simple spell into something that can compete for the title =D
The main idea of it's use is for spells, in my spell, if the units step on the symbol they take damage. How ? Well, the units of the group have immolation =D

I may do a few more spells using this system and post them here =P

Quote:
I don't mean, how do you call the constructors/destructors, I mean what does it do with these geometric shapes? Upon further reading it seems like it creates units (and adds them to a group) in geometric patterns, is this correct?

Quote:
you do LOTS of calculations in the timer callbacks which can be before
Pleas exemplify them so I can improve the code.

Quote:
and you should stop using degrees ....use radians ...
WHat!? No. Just No. I will not force my user to use and think "radian" like just to win a few nanoseconds. I will not decrease readability for performance, it is not worth it in this case.

Quote:
I would make this system allow for user input in the following way:

* There is a basic struct called a "shape" that has a few basic methods:
o Create
o Destroy
o Move
o Rotate
Quote:
Rather than making it a struct you could probably just make it an interface.
I agree with xombie, wouldn't it make more sense to have an interface instead of a structure?
Btw, I dunno how to use interfaces on structures =S

Quote:
Then, in your system you'd provide a few of the basic ones in the main system (Line, circle), and then have a separate library that would house all the 'complex' shapes. You could add the ones you have now, and then allow the user to create his or her own new shapes and add them to the second library.
So, I would need 1 interface library, 1 basic library and 1 complex library rigth?
3 libraries for 1 system mmm =S

I am willing to make the required changes you guys find necessary, but I need specific instructions, I need to know exactly what you want me to upgrade.

Btw, I will give rep+ to people who help me and give credits as well.
Thx for the posts guys!
03-12-2009, 01:24 PM#10
akolyt0r
Quote:
Originally Posted by Flame_Phoenix
#1 Pleas exemplify them so I can improve the code.


#2 WHat!? No. Just No. I will not force my user to use and think "radian" like just to win a few nanoseconds. I will not decrease readability for performance, it is not worth it in this case.

#1 example: ( 360.00 / (data.radius / data.unitSize )) * bj_DEGTORAD
#2: it doesnt decrease readability ...and if the user still wants to use radians they can call your system with something like 90*bj_DEGTORAD, beside they dont even need to call methods of your "system" ...with any angles as parameter ...
So its all about you being stubborn .. 0.o
03-12-2009, 03:09 PM#11
Flame_Phoenix
@akolyt0r:

I will check point 1 and see if I can avoid some calculations. Thx for pointing it out.
About point 2, making the conversion is the duty of the system not of the user, the system is meant to be simple to use, forcing a user to pass radians as arguments is a stupidity having in mind degrees are much easier to use.
Also, the user NEEDS to pass angles as parameters, just check my Xspot.createCustom method ...
Seriously, if I am so stubborn then perhaps you could tell me why I am taking opinions from Pyro and xombie? Just because I don't accept all you suggestions it doesn't mean I am stubborn, live with it.
03-12-2009, 03:22 PM#12
xombie
Parameters for user-end functions/methods should usually take an angle measured in degrees, while anything internal should use radians for processing speed.
03-12-2009, 03:30 PM#13
akolyt0r
Quote:
Originally Posted by Flame_Phoenix
@akolyt0r:

I will check point 1 and see if I can avoid some calculations. Thx for pointing it out.
About point 2, making the conversion is the duty of the system not of the user, the system is meant to be simple to use, forcing a user to pass radians as arguments is a stupidity having in mind degrees are much easier to use.
Also, the user NEEDS to pass angles as parameters, just check my Xspot.createCustom method ...
Seriously, if I am so stubborn then perhaps you could tell me why I am taking opinions from Pyro and xombie? Just because I don't accept all you suggestions it doesn't mean I am stubborn, live with it.

radians arent difficult to use ...and i wouldnt use that system when i use radians basically everywhere ...since all functions which matter for angles use radians (except unitfacing stuff) ...so its pretty LOGICAL to use radians...

users of this should really be wise enough to do a [ljass]X*bj_DEGTORAD[ljass] ...when they really want to use degrees and not radians ...

But well if you dont believe that ...dont do it for your public methods (the ones the user can call..)

But you should at least get rid of degrees in methods the user wont call, really...
for example in Circle.createEff.. shouldnt that method be private by the way ?!
03-12-2009, 03:57 PM#14
Flame_Phoenix
Quote:
But you should at least get rid of degrees in methods the user wont call, really...
for example in Circle.createEff.. shouldnt that method be private by the way ?!
It makes the code for me hard to read lol....
and yes, it should definitely be private, I don't want people to call that method, it is for exclusive use of the create method and that's all.
03-12-2009, 04:08 PM#15
akolyt0r
Quote:
Originally Posted by Flame_Phoenix
It makes the code for me hard to read lol....
Ah, thats the problem ;)
Well than change it later, when you think you are finished with the development of the rest of the system...