diff --git a/cloth.go b/cloth.go index a97f331..62de5bc 100644 --- a/cloth.go +++ b/cloth.go @@ -86,7 +86,7 @@ func (cloth *Cloth) Update(gtx layout.Context, mouse *Mouse, delta float64) { } for _, c := range cloth.constraints { - if c.isSelected { + if c.p1.isActive { c.Update(gtx, cloth, mouse) } } @@ -97,7 +97,7 @@ func (cloth *Cloth) Update(gtx layout.Context, mouse *Mouse, delta float64) { // For performance reasons we draw the sticks as a single clip path instead of multiple clips paths. // The performance improvement is considerable compared to the multiple clip paths rendered separately. for _, c := range cloth.constraints { - if c.isSelected { + if c.p1.isActive { // We are using `clip.Outline` instead of `clip.Stroke` for performance reasons. // But we need to draw the full outline of the stroke. path.MoveTo(f32.Pt(float32(c.p1.x), float32(c.p1.y))) @@ -121,7 +121,8 @@ func (cloth *Cloth) Update(gtx layout.Context, mouse *Mouse, delta float64) { // because the color used for highlighting the selected area // should be different than the cloth's default color. for _, c := range cloth.constraints { - if c.isActive && c.isSelected { + if (c.p1.isActive && c.p1.highlighted) && + (c.p2.isActive && c.p2.highlighted) { path.Begin(gtx.Ops) path.MoveTo(f32.Pt(float32(c.p1.x), float32(c.p1.y))) @@ -140,8 +141,6 @@ func (cloth *Cloth) Update(gtx layout.Context, mouse *Mouse, delta float64) { paint.FillShape(gtx.Ops, c.color, clip.Outline{ Path: path.End(), }.Op()) - - c.isActive = false } } } diff --git a/constraint.go b/constraint.go index 27f3858..ff12b27 100644 --- a/constraint.go +++ b/constraint.go @@ -8,25 +8,21 @@ import ( ) type Constraint struct { - p1, p2 *Particle - length float64 - isSelected bool - isActive bool - color color.NRGBA + p1, p2 *Particle + length float64 + color color.NRGBA } // NewConstraint creates a new constraint between two points/particles. // The constraint actually is a stick which connects two points. func NewConstraint(p1, p2 *Particle, length float64, col color.NRGBA) *Constraint { return &Constraint{ - p1, p2, length, true, false, col, + p1, p2, length, col, } } // Update updates the stick between two points by resolving the constraints between them. func (c *Constraint) Update(gtx layout.Context, cloth *Cloth, mouse *Mouse) { - c.p1.constraint = c - dx := c.p1.x - c.p2.x dy := c.p1.y - c.p2.y dist := math.Sqrt(dx*dx + dy*dy) @@ -38,7 +34,7 @@ func (c *Constraint) Update(gtx layout.Context, cloth *Cloth, mouse *Mouse) { // The threshold is the distance between the two points. if mouse.getDragging() { if dist > 150 { - c.p1.removeConstraint(cloth) + c.removeConstraint(cloth) } } @@ -56,3 +52,13 @@ func (c *Constraint) Update(gtx layout.Context, cloth *Cloth, mouse *Mouse) { c.p2.y -= offsetY } } + +// removeConstraint removes a specific constraint (stick) from the collection, stored into a slice. +func (c *Constraint) removeConstraint(cloth *Cloth) { + for idx, constraint := range cloth.constraints { + if c == constraint { + cloth.constraints = append(cloth.constraints[:idx], cloth.constraints[idx+1:]...) + break + } + } +} diff --git a/particle.go b/particle.go index ae15eeb..37d050e 100644 --- a/particle.go +++ b/particle.go @@ -14,7 +14,7 @@ const ( clothTearDist = 60 clothPinDist = 4 gravityForce = 600 - defFocusArea = 40 + defFocusArea = 50 minFocusArea = 30 maxFocusArea = 150 mouseDragForce = 4.2 @@ -23,15 +23,16 @@ const ( // Particle holds the basic components of the particle system. type Particle struct { - x, y float64 - px, py float64 - vx, vy float64 - friction float64 - elasticity float64 - dragForce float64 - pinX bool - color color.NRGBA - constraint *Constraint + x, y float64 + px, py float64 + vx, vy float64 + friction float64 + elasticity float64 + dragForce float64 + pinX bool + isActive bool + highlighted bool + color color.NRGBA } // NewParticle initializes a new Particle. @@ -39,6 +40,8 @@ func NewParticle(x, y float64, col color.NRGBA) *Particle { p := &Particle{ x: x, y: y, px: x, py: y, color: col, } + p.isActive = true + p.highlighted = false p.elasticity = 25.0 p.dragForce = mouseDragForce @@ -77,6 +80,8 @@ func (p *Particle) draw(gtx layout.Context, x, y, r float32) { // update is an internal method to update the cloth system using Verlet integration. func (p *Particle) update(gtx layout.Context, mouse *Mouse, dt float64) { + p.highlighted = false + if p.pinX { return } @@ -120,14 +125,14 @@ func (p *Particle) update(gtx layout.Context, mouse *Mouse, dt float64) { focusArea = minFocusArea } - if p.constraint != nil && dist < float64(focusArea) { - p.constraint.isActive = true + if dist < float64(focusArea) { + p.highlighted = true } // With right click we can tear up the cloth at the mouse position. if mouse.getRightButton() { - if p.constraint != nil && dist < float64(focusArea) { - p.constraint.isSelected = false + if dist < float64(focusArea) { + p.isActive = false } } @@ -172,16 +177,6 @@ func (p *Particle) update(gtx layout.Context, mouse *Mouse, dt float64) { p.vx, p.vy = 0.0, 0.0 } -// removeConstraint removes a specific constraint (stick) from the collection, stored into a slice. -func (p *Particle) removeConstraint(cloth *Cloth) { - for idx, c := range cloth.constraints { - if c == p.constraint { - cloth.constraints = append(cloth.constraints[:idx], cloth.constraints[idx+1:]...) - break - } - } -} - // increaseForce increases the dragging force. func (p *Particle) increaseForce(m *Mouse) { p.dragForce += m.force