Help with vectors and limiting speed

Kenny

Back for now.
Reaction score
202
So after attempting to use processing, I decided to come back to wc3 and attempt to implement vectors in a projectile system I am making.

My question is:

Does anyone know how to limit the speed of a projectile that uses vectors to move?

I will try to explain it for now, as I can't post the code until I get on another computer...

Basically, I use a vector for the position of the projectile, a vector for projectile velocity and a vector for gravity.

Projectiles can bounce off the ground and walls etc, and depending on the angle at which they bounce, the velocity vector will get increased / decreased dramatically, resulting in either speeding up the projectile or slowing it down.

What I want is to keep the projectile at a constant speed while it moves, so bounces and angles to not affect the speed.

I will post my projectile system and vector system when I can. :)
 

Anachron

New Member
Reaction score
53
I don't even understand the question. When you calculate the speed, why can't you not just not calculate it?
 

Kenny

Back for now.
Reaction score
202
It is really confusing to explain without the script.

When a projectile is "fired", the system sets the x, y, and z velocity of the projectile.

Something like:

JASS:
set this.vel.x = Sin(this.phi) * Cos(this.theta) * this.speed
set this.vel.y = Sin(this.phi) * Sin(this.theta) * this.speed
set this.vel.z = Sin(this.phi) * this.speed

// this.phi = XYZ angle (includes z angle)
// this.theta = XY angle (normal angle used commonly)


I thought using [ljass]* this.speed[/ljass] would set the speed of the projectile, however, when the velocity is added to the position of the unit after bouncing, it increases or decreases, affecting the overall speed.
 

Kenny

Back for now.
Reaction score
202
Okay, I can show the script now. :)

The projectile system:

JASS:
library ProjSys initializer Init requires AIDS, T32, Event, Vector, AutoFly

    //----------------------------------------------------------
    //
    // To do list:
    //    - Re-write method operators (get rid of useless ones).                  [90% done]
    //    - Attempt to add vertical collision for units.                          [90% done]
    //    - Add method for resetting velocities (for homing projectiles).         [90% done]
    //    - Re-write ProjInterface to work with new operators.                    [90% done]
    //
    //-----------------------------------------------------------

    globals
        private constant integer PROJ_DUMMY_ID       = 'ewsp'
        private constant integer DEFAULT_MAX_BOUNCES = 1
        private constant real    DEFAULT_GRAVITY     = -9.81
        private constant real    DEFAULT_RESTITUTION = 0.90
        private constant real    DEFAULT_UNIT_COLL   = 128.00
        private constant real    DEFAULT_PROJ_COLL   = 64.00
        
        private Event CollisionEvent = 0    
        Projectile    FirstProjData  = 0
        Projectile    SecondProjData = 0
    endglobals
    
    native UnitAlive takes unit id returns boolean
    
    private interface ProjInterface
        method onStart  takes nothing returns nothing defaults nothing
        method onLoop   takes nothing returns nothing defaults nothing
        method onFinish takes nothing returns nothing defaults nothing
        
        method onGroundImpact takes nothing returns nothing defaults nothing
        method onProjImpact   takes nothing returns nothing defaults nothing
        method onUnitImpact   takes unit whichUnit returns nothing defaults nothing
    endinterface
    
    private struct ProjData extends array
        //! runtextmacro AIDS()
        
        Projectile data
        
        private static method AIDS_filter takes unit whichUnit returns boolean
            return GetUnitTypeId(whichUnit) == PROJ_DUMMY_ID
        endmethod
        
        private method AIDS_onCreate takes nothing returns nothing
            set this.data = 0
        endmethod
        
        private method AIDS_onDestroy takes nothing returns nothing
            set this.data = 0
        endmethod
        
    endstruct
    
    struct Projectile extends ProjInterface
    
        // Public members for users.
        unit    caster         = null
        real    timedLife      = -1.00
        real    unitCollision  = DEFAULT_UNIT_COLL
        real    projCollision  = DEFAULT_PROJ_COLL
        real    restitution    = DEFAULT_RESTITUTION
        integer maxBounces     = DEFAULT_MAX_BOUNCES
        boolean pauseProj      = false
        boolean collideable    = false
    
        // Readonly members for users.
        readonly unit   proj   = null
        readonly real   theta  = 0.00
        readonly real   phi    = 0.00
        readonly real   speed  = 0.00
        readonly vector pos    = 0
        readonly vector vel    = 0

        // Private members, not for users. Some have method operators.
        private player  own    = null
        private effect  sfx    = null
        private vector  grav   = 0
        private boolean stop   = false
        private group   dmged
        
        // Static members for grouping units.
        private static vector   tempVect  = 0
        private static thistype tempData  = 0
        private static boolexpr tempFilt  = null
        private static group    tempGroup = null
        
        // Method operators for cleaner interface.
        static method operator[] takes unit whichUnit returns thistype
            return ProjData[whichUnit].data
        endmethod
        
        method operator owner= takes player whichPlayer returns nothing
            set this.own = whichPlayer
            call SetUnitOwner(this.proj,whichPlayer,true)
        endmethod
        
        method operator owner takes nothing returns player
            return this.own
        endmethod
        
        method operator scaleSize= takes real value returns nothing
            call SetUnitScale(this.proj,value,0.00,0.00)
        endmethod
        
        method operator effectPath= takes string path returns nothing
           if this.sfx != null then
               call DestroyEffect(this.sfx)
           endif
           
           if path == "" then
               set this.sfx = null
           else
               set this.sfx = AddSpecialEffectTarget(path,this.proj,"origin")
           endif
        endmethod
        
        method operator gravity= takes real value returns nothing
            if this.grav != 0 then
                call this.grav.destroy()
            endif
            set this.grav = vector.create(0.00,0.00,((value * T32_PERIOD * T32_PERIOD) / 2.00))
        endmethod
        
        method operator isTerminated takes nothing returns boolean
            return this.stop
        endmethod
        
        private method destroy takes nothing returns nothing
            if this.onFinish.exists then
                call this.onFinish()
            endif
            
            set ProjData[this.proj].data = 0
            
            call this.pos.destroy()
            call this.vel.destroy()
            call this.grav.destroy()
            
            if this.sfx != null then
                call DestroyEffect(this.sfx)
            endif
            
            call RemoveUnit(this.proj)
            call GroupClear(this.dmged)
            
            set this.caster = null
            set this.proj = null
            set this.sfx  = null
            
            call this.deallocate()
        endmethod
        
        private static method unitFilter takes nothing returns boolean
            local thistype this = thistype.tempData
            local unit     filt = GetFilterUnit()
            
            if UnitAlive(filt) and not IsUnitInGroup(filt,this.dmged) then
                if this.onUnitImpact.exists then
                    call this.onUnitImpact(filt)
                endif
                call GroupAddUnit(this.dmged,filt)
            endif
            
            set filt = null
            
            return false
        endmethod
        
        private method projCollisions takes nothing returns nothing
            local thistype that = thistype(0).next
            local vector   v    = 0
            
            loop
                exitwhen that == 0
                if this != that and that.collideable then
                    if that.pos.isInSphere(this.pos,this.projCollision) and this.pos.isInSphere(that.pos,that.projCollision) then
                        if this.onProjImpact.exists then
                            call this.onProjImpact()
                        endif
                        if that.onProjImpact.exists then
                            call that.onProjImpact()
                        endif
                        set FirstProjData  = this
                        set SecondProjData = that
                        call CollisionEvent.fire()
                    endif
                endif
                set that = that.next
            endloop
        endmethod
        
        private method periodic takes nothing returns nothing
            local vector v = 0
            
            if this.stop then
                call this.stopPeriodic()
                call this.destroy()
            endif
            
            if this.collideable then
                call this.projCollisions()
            endif
            
            if this.onLoop.exists then
                call this.onLoop()
            endif
            
            if not this.pauseProj then
                if this.timedLife > -1.0 then
                    set this.timedLife = this.timedLife - T32_PERIOD
                    if this.timedLife <= 0.00 then
                        set this.stop = true
                    endif
                endif
                
                call this.vel.add(this.grav)
                call this.pos.add(this.vel)
                call this.vel.add(this.grav)
                
                call thistype.tempVect.getTerrainPoint(this.pos.x,this.pos.y)
                
                call SetUnitX(this.proj,this.pos.x)
                call SetUnitY(this.proj,this.pos.y)
                call SetUnitFlyHeight(this.proj,this.pos.z - thistype.tempVect.z,0.00)
                
                set thistype.tempData = this
                call GroupEnumUnitsInRange(thistype.tempGroup,this.pos.x,this.pos.y,this.unitCollision,thistype.tempFilt)
                
                if thistype.tempVect.z >= this.pos.z then
                    call thistype.tempVect.getTerrainNormal(thistype.tempVect.x,thistype.tempVect.y,16.00)
                    if vector.dotProduct(this.vel,thistype.tempVect) <= 0.00 then
                        set v = vector.projectionVector(this.vel,thistype.tempVect)
                        call v.mult(-this.restitution)
                        call this.vel.add(v)
                        call v.destroy()
                        
                        if this.maxBounces == 1 then
                            set this.stop = true
                        else
                            set this.maxBounces = this.maxBounces - 1
                        endif

                        if this.onGroundImpact.exists then
                            call this.onGroundImpact()
                        endif
                    endif
                endif
            endif
        endmethod
        
        implement T32x
        
        method terminate takes nothing returns nothing
            set this.effectPath = ""
            set this.stop = true
        endmethod
        
        method isUnitInHeightRange takes unit whichUnit returns boolean
            return SquareRoot((this.pos.z - GetUnitFlyHeight(whichUnit)) * (this.pos.z - GetUnitFlyHeight(whichUnit))) <= this.unitCollision
        endmethod
        
        method adjustTargetCoords takes real xPos, real yPos, real zPos returns nothing
            set this.theta = Atan2((yPos - this.pos.y),(xPos - this.pos.x))
            set this.phi   = Atan2(SquareRoot((xPos - this.pos.x) * (xPos - this.pos.x) + (yPos - this.pos.y) * (yPos - this.pos.y)),(zPos - this.pos.z))
        
            set this.vel.x = Sin(this.phi) * Cos(this.theta) * this.speed
            set this.vel.y = Sin(this.phi) * Sin(this.theta) * this.speed
            set this.vel.z = Cos(this.phi) * this.speed
        endmethod
        
        method scaleVelocity takes real value returns nothing
            local vector v = vector.sum(this.pos,this.vel)
            
            set this.theta = Atan2((v.y - this.pos.y),(v.x - this.pos.x))
            set this.phi   = Atan2(SquareRoot((v.x - this.pos.x) * (v.x - this.pos.x) + (v.y - this.pos.y) * (v.y - this.pos.y)),(v.z - this.pos.z))
            set this.speed   = this.speed * value
            
            set this.vel.x = Sin(this.phi) * Cos(this.theta) * this.speed
            set this.vel.y = Sin(this.phi) * Sin(this.theta) * this.speed
            set this.vel.z = Cos(this.phi) * this.speed
            
            call v.destroy()
        endmethod
        
        method start takes real xPos, real yPos, real zPos, real speed returns nothing
            set this.theta = Atan2((yPos - this.pos.y),(xPos - this.pos.x))
            set this.phi   = Atan2(SquareRoot((xPos - this.pos.x) * (xPos - this.pos.x) + (yPos - this.pos.y) * (yPos - this.pos.y)),(zPos - this.pos.z))
            set this.speed = speed * T32_PERIOD
            set this.vel   = vector.create(Sin(this.phi) * Cos(this.theta) * this.speed,Sin(this.phi) * Sin(this.theta) * this.speed,Cos(this.phi) * this.speed)
            
            if this.grav == 0 then
                set this.grav = vector.create(0.00,0.00,(DEFAULT_GRAVITY * 4.00) * T32_PERIOD)
            endif
            
            if this.onStart.exists then
                call this.onStart()
            endif
            
            call this.startPeriodic()
        endmethod
        
        static method create takes real xPos, real yPos, real zPos, real facing returns thistype
            local thistype this = thistype.allocate()
            
            set this.proj = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),PROJ_DUMMY_ID,xPos,yPos,facing * bj_RADTODEG)
            set this.pos  = vector.create(xPos,yPos,zPos)
            
            call SetUnitX(this.proj,xPos)
            call SetUnitY(this.proj,yPos)
            call thistype.tempVect.getTerrainPoint(xPos,yPos)
            call SetUnitFlyHeight(this.proj,zPos - thistype.tempVect.z,0.00)
            
            if this.dmged == null then
                set this.dmged = CreateGroup()
            endif
            
            set ProjData[this.proj].data = this
            
            return this
        endmethod 
        
        private static method onInit takes nothing returns nothing
            set thistype.tempVect  = vector.create(0.00,0.00,0.00)
            set thistype.tempFilt  = Filter(function thistype.unitFilter)
            set thistype.tempGroup = CreateGroup()
        endmethod
        
    endstruct
    
    function GroupEnumProjectilesInRange takes group whichGroup, real x, real y, real z, real radius returns nothing
        local Projectile p = Projectile(0).next
        
        loop
            exitwhen p == 0
            if p.pos.isInSphereEx(x,y,z,radius) then
                call GroupAddUnit(whichGroup,p.proj)
            endif
            set p = p.next
        endloop
    endfunction
    
    function IsUnitInHeightRange takes real projHeight, real unitHeight, real unitCollision returns boolean
        return SquareRoot((projHeight - unitHeight) * (projHeight - unitHeight)) <= unitCollision
    endfunction
    
    function TriggerRegisterProjectileCollisionEvent takes trigger whichTrigger returns nothing
        call CollisionEvent.register(whichTrigger)
    endfunction
    
    private function Init takes nothing returns nothing
        set CollisionEvent = Event.create()
    endfunction
    
endlibrary


The vector system:

JASS:
library Vector

    struct vector
    
        real x = 0.00
        real y = 0.00
        real z = 0.00
        
        private static location loc = Location(0.00,0.00)
        
        //---------------------------------------------------------------------\\
        // Method to create a vector.
        static method create takes real xPos, real yPos, real zPos returns thistype
            local thistype this = thistype.allocate()
            
            set this.x = xPos
            set this.y = yPos
            set this.z = zPos
            
            return this
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods for adding two vectors together.
        static method sum takes vector a, vector b returns thistype
            local thistype this = thistype.allocate()
            
            set this.x = a.x + b.x
            set this.y = a.y + b.y
            set this.z = a.z + b.z
            
            return this
        endmethod
        
        method add takes vector a returns nothing
            set this.x = this.x + a.x
            set this.y = this.y + a.y
            set this.z = this.z + a.z
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods for subtracting two vectors from eachother.
        static method diff takes vector a, vector b returns thistype
            local thistype this = thistype.allocate()
            
            set this.x = a.x - b.x
            set this.y = a.y - b.y
            set this.z = a.z - b.z
            
            return this
        endmethod
        
        method sub takes vector a returns nothing
            set this.x = this.x - a.x
            set this.y = this.y - a.y
            set this.z = this.z - a.z
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods for getting the cross and dot products of vectors.
        static method crossProduct takes vector a, vector b returns thistype
            local thistype this = thistype.allocate()
            
            set this.x = a.y * b.z - a.z * b.y
            set this.y = a.z * b.x - a.x * b.z
            set this.z = a.x * b.y - a.y * b.x
            
            return this
        endmethod
        
        static method dotProduct takes vector a, vector b returns real
            return a.x * b.x + a.y * b.y + a.z * b.z
        endmethod
        
        //---------------------------------------------------------------------\\
        // Multiplies a vector depending on a given value.
        method mult takes real value returns nothing
            if value < 0.00 then
                set value = value - 1.00 // Only temporary.
            elseif value > 0.00 then
                set value = value + 1.00
            endif
            
            set this.x = this.x * value
            set this.y = this.y * value
            set this.z = this.z * value
        endmethod
        
        method div takes real value returns nothing
            set this.x = this.x / value
            set this.y = this.y / value
            set this.z = this.z / value
        endmethod
        
        //---------------------------------------------------------------------\\  
        // Checks if a vector is within a certain radius of another vector or xyz location.    
        method isInSphereEx takes real x, real y, real z, real radius returns boolean
            if radius * radius < ((this.x - x) * (this.x - x) + (this.y - y) * (this.y - y) + (this.z - z) * (this.z - z)) then
                return false
            endif
            return true
        endmethod
        
        method isInSphere takes vector a, real radius returns boolean
            return this.isInSphereEx(a.x,a.y,a.z,radius)
        endmethod
        
        //---------------------------------------------------------------------\\ 
        // Methods to project vectors onto other vectors.
        static method projectionVector takes vector a, vector b returns thistype
            local thistype this   = thistype.allocate()
            local real     length = b.x * b.x + b.y * b.y + b.z * b.z
            
            if length == 0.00 then
                call this.destroy()
                return 0
            endif
            
            set length = (a.x * b.x + a.y * b.y + a.z * b.z) / length
            set this.x = b.x * length
            set this.y = b.y * length
            set this.z = b.z * length
            
            return this
        endmethod
        
        method projectVector takes vector a returns nothing
            local real length = a.x * a.x + a.y * a.y + a.z * a.z
            
            if length == 0.00 then
                return
            endif
            
            set length = (this.x * a.x + this.y * a.y + this.z * a.z) / length
            set this.x = a.x * length
            set this.y = a.y * length
            set this.z = a.z * length
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods to get the angle between two vectors (includes z height).
        static method angleBetween takes vector a, vector b returns real
            local real length = SquareRoot(a.x * a.x + a.y * a.y + a.z * a.z) * SquareRoot(b.x * b.x + b.y * b.y + b.z * b.z)
            
            if length == 0.00 then
                return 0.00
            endif
            
            return Acos((a.x * b.x + a.y * b.y + a.z * b.z) / length)
        endmethod
        
        method angleTo takes vector a returns real
            local real length = SquareRoot(this.x * this.x + this.y * this.y + this.z * this.z) * SquareRoot(a.x * a.x + a.y * a.y + a.z * a.z)
            
            if length == 0.00 then
                return 0.00
            endif
            
            return Acos((this.x * a.x + this.y * a.y + this.z * a.z) / length)
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods to set and get the magnitude (length) of a vector.
        method setMagnitude takes real value returns nothing
            local real length = SquareRoot(this.x * this.x + this.y * this.y + this.z * this.z)
            
            if length == 0.00 then
                return
            endif
            
            set length = length / length
            set this.x = this.x * length
            set this.y = this.y * length
            set this.z = this.z * length
        endmethod
        
        method getMagnitude takes nothing returns real
            return SquareRoot(this.x * this.x + this.y * this.y + this.z * this.z)
        endmethod
        
        //---------------------------------------------------------------------\\ 
        // Methods to check for terrain heights and terrain averages.        
        method getTerrainPoint takes real x, real y returns nothing
            call MoveLocation(vector.loc,x,y)
            set this.x = x
            set this.y = y
            set this.z = GetLocationZ(vector.loc)
        endmethod
        
        method getTerrainNormal takes real x, real y, real sampleRadius returns nothing
            local real zx = 0.00
            local real zy = 0.00
            
            call MoveLocation(vector.loc,x - sampleRadius,y)
            set zx = GetLocationZ(vector.loc)
            call MoveLocation(vector.loc,x + sampleRadius,y)
            set zx = zx - GetLocationZ(vector.loc)
            call MoveLocation(vector.loc,x,y - sampleRadius)
            set zy = GetLocationZ(vector.loc)
            call MoveLocation(vector.loc,x,y + sampleRadius)
            set zy = zy - GetLocationZ(vector.loc)
            
            set sampleRadius = 2.00 * sampleRadius
            
            set this.x = zx * sampleRadius
            set this.y = zy * sampleRadius
            set this.z = sampleRadius * sampleRadius
        endmethod
        
    endstruct
    
endlibrary


The problem occurs here:

JASS:
...
                if thistype.tempVect.z >= this.pos.z then
                    call thistype.tempVect.getTerrainNormal(thistype.tempVect.x,thistype.tempVect.y,16.00)
                    if vector.dotProduct(this.vel,thistype.tempVect) <= 0.00 then
                        set v = vector.projectionVector(this.vel,thistype.tempVect)
                        call v.mult(-this.restitution)
                        call this.vel.add(v)
                        call v.destroy()
                        
                        if this.maxBounces == 1 then
                            set this.stop = true
                        else
                            set this.maxBounces = this.maxBounces - 1
                        endif

                        if this.onGroundImpact.exists then
                            call this.onGroundImpact()
                        endif
                    endif
                endif
...


The above works correctly (for the most part), I am just wondering if anyone has a way to constrain the velocity of the projectile to [ljass]this.speed[/ljass].
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top