Skip to content

Commit

Permalink
Fixed cloth constraints issue on mouse highlight
Browse files Browse the repository at this point in the history
  • Loading branch information
esimov committed Jan 9, 2023
1 parent 57c96a1 commit 699d51c
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 38 deletions.
9 changes: 4 additions & 5 deletions cloth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand All @@ -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)))
Expand All @@ -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)))
Expand All @@ -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
}
}
}
Expand Down
24 changes: 15 additions & 9 deletions constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
}

Expand All @@ -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
}
}
}
43 changes: 19 additions & 24 deletions particle.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const (
clothTearDist = 60
clothPinDist = 4
gravityForce = 600
defFocusArea = 40
defFocusArea = 50
minFocusArea = 30
maxFocusArea = 150
mouseDragForce = 4.2
Expand All @@ -23,22 +23,25 @@ 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.
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

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 699d51c

Please sign in to comment.