swf2ass-go/types/shapes/Shape.go

222 lines
4.9 KiB
Go
Raw Normal View History

package shapes
2023-11-20 05:20:31 +00:00
import (
2023-11-27 09:25:19 +00:00
"fmt"
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/math"
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/records"
)
2023-11-20 05:20:31 +00:00
2023-11-26 19:34:44 +00:00
type Shape []records.Record
2023-11-20 05:20:31 +00:00
2023-11-26 19:34:44 +00:00
func (s Shape) ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) (newShape Shape) {
newShape = make(Shape, 0, len(s))
for _, edge := range s {
newShape = append(newShape, edge.ApplyMatrixTransform(transform, applyTranslation))
2023-11-20 05:20:31 +00:00
}
2023-11-26 19:34:44 +00:00
return newShape
2023-11-20 05:20:31 +00:00
}
2023-11-26 19:34:44 +00:00
func (s Shape) Start() math.Vector2[float64] {
if len(s) == 0 {
return math.NewVector2[float64](0, 0)
}
2023-11-26 19:34:44 +00:00
return s[0].GetStart()
}
2023-11-26 19:34:44 +00:00
func (s Shape) End() math.Vector2[float64] {
if len(s) == 0 {
return math.NewVector2[float64](0, 0)
2023-11-20 05:20:31 +00:00
}
2023-11-26 19:34:44 +00:00
return s[len(s)-1].GetEnd()
2023-11-20 05:20:31 +00:00
}
2023-11-26 19:34:44 +00:00
func (s Shape) IsFlat() bool {
for _, e := range s {
if !e.IsFlat() {
return false
}
2023-11-20 05:20:31 +00:00
}
2023-11-26 19:34:44 +00:00
return true
2023-11-20 05:20:31 +00:00
}
2023-11-26 19:34:44 +00:00
func (s Shape) IsClosed() bool {
2023-11-20 05:20:31 +00:00
return s.Start().Equals(s.End())
}
2023-11-26 19:34:44 +00:00
func (s Shape) Reverse() (r Shape) {
r = make(Shape, len(s))
for i, e := range s {
r[len(s)-1-i] = e.Reverse()
2023-11-25 13:18:52 +00:00
}
return r
}
2023-11-26 19:34:44 +00:00
func (s Shape) Merge(o Shape) (r Shape) {
r = make(Shape, 0, len(s)+len(o))
r = append(r, s...)
r = append(r, o...)
2023-11-20 05:20:31 +00:00
return r
}
2023-11-29 00:04:52 +00:00
func (s Shape) BoundingBox() Rectangle[float64] {
rect := Rectangle[float64]{}
if len(s) == 0 {
return rect
}
rect.TopLeft, rect.BottomRight = s[0].BoundingBox()
for _, r := range s[1:] {
tl, br := r.BoundingBox()
rect.TopLeft = rect.TopLeft.Min(tl)
rect.BottomRight = rect.BottomRight.Max(br)
}
return rect
}
// Flatten Converts all non-linear records into line segments and returns a new Shape
2023-11-26 19:34:44 +00:00
func (s Shape) Flatten() (r Shape) {
if s.IsFlat() {
2023-11-20 05:20:31 +00:00
return s
}
2023-11-26 19:34:44 +00:00
r = make(Shape, 0, len(s)*4)
2023-11-20 05:20:31 +00:00
2023-11-26 19:34:44 +00:00
for _, e := range s {
r = append(r, records.FlattenRecord(e, 1)...)
2023-11-20 05:20:31 +00:00
}
return r
}
type RecordCorrespondence struct {
Original records.Record
Flattened []records.Record
}
func (s Shape) FlattenWithCorrespondence() (r Shape, ix []RecordCorrespondence) {
if s.IsFlat() {
return s, nil
}
r = make(Shape, 0, len(s)*4)
for _, e := range s {
flattened := records.FlattenRecord(e, 1)
if len(flattened) > 1 {
ix = append(ix, RecordCorrespondence{
Original: e,
Flattened: flattened,
})
}
r = append(r, flattened...)
}
return r, ix
}
2023-11-26 19:34:44 +00:00
func (s Shape) Equals(o Shape) bool {
if len(s) != len(o) {
2023-11-20 05:20:31 +00:00
return false
}
2023-11-26 19:34:44 +00:00
for i := range s {
if !s[i].Equals(o[i]) {
2023-11-20 05:20:31 +00:00
return false
}
}
return true
}
2023-11-27 09:25:19 +00:00
func (s Shape) String() (r string) {
if len(s) == 0 {
return ""
}
var pos math.Vector2[float64]
for _, rec := range s {
if !rec.GetStart().Equals(pos) {
r += fmt.Sprintf("m %s\n", rec.GetStart())
}
r += rec.String() + "\n"
pos = rec.GetEnd()
}
return r
}
2023-11-26 19:34:44 +00:00
func IterateMorphShape(start, end Shape) (r []records.RecordPair) {
var prevStart, prevEnd records.Record
2023-11-26 19:34:44 +00:00
for len(start) > 0 && len(end) > 0 {
startEdge := start[0]
endEdge := end[0]
advanceStart := true
advanceEnd := true
if prevStart != nil && !prevStart.GetEnd().Equals(startEdge.GetStart()) {
advanceStart = false
2023-11-26 19:47:12 +00:00
startEdge = records.MoveRecord{
To: startEdge.GetStart(),
Start: prevStart.GetEnd(),
}
}
if prevEnd != nil && !prevEnd.GetEnd().Equals(endEdge.GetStart()) {
advanceEnd = false
2023-11-26 19:47:12 +00:00
endEdge = records.MoveRecord{
To: endEdge.GetStart(),
Start: prevEnd.GetEnd(),
}
}
if startEdge.SameType(endEdge) {
r = append(r, records.RecordPair{startEdge, endEdge})
} else {
2023-11-26 19:47:12 +00:00
aLineRecord, aIsLineRecord := startEdge.(records.LineRecord)
aMoveRecord, aIsMoveRecord := startEdge.(records.MoveRecord)
aQuadraticCurveRecord, aIsQuadraticCurveRecord := startEdge.(records.QuadraticCurveRecord)
bLineRecord, bIsLineRecord := endEdge.(records.LineRecord)
bMoveRecord, bIsMoveRecord := endEdge.(records.MoveRecord)
bQuadraticCurveRecord, bIsQuadraticCurveRecord := endEdge.(records.QuadraticCurveRecord)
if aIsLineRecord && bIsQuadraticCurveRecord {
startEdge = records.QuadraticCurveFromLineRecord(aLineRecord)
r = append(r, records.RecordPair{startEdge, bQuadraticCurveRecord})
} else if aIsQuadraticCurveRecord && bIsLineRecord {
endEdge = records.QuadraticCurveFromLineRecord(bLineRecord)
r = append(r, records.RecordPair{aQuadraticCurveRecord, endEdge})
} else if aIsMoveRecord && !bIsMoveRecord {
2023-11-26 19:47:12 +00:00
endEdge = records.MoveRecord{
To: endEdge.GetStart(),
Start: endEdge.GetStart(),
}
r = append(r, records.RecordPair{aMoveRecord, endEdge})
advanceEnd = false
} else if !aIsMoveRecord && bIsMoveRecord {
2023-11-26 19:47:12 +00:00
startEdge = records.MoveRecord{
To: startEdge.GetStart(),
Start: startEdge.GetStart(),
}
r = append(r, records.RecordPair{startEdge, bMoveRecord})
advanceStart = false
} else {
panic("incompatible")
}
}
if advanceStart {
2023-11-26 19:34:44 +00:00
start = start[1:]
}
if advanceEnd {
2023-11-26 19:34:44 +00:00
end = end[1:]
}
prevStart = startEdge
prevEnd = endEdge
}
2023-11-26 19:34:44 +00:00
if len(start) != 0 || len(end) != 0 {
panic("incompatible result")
}
return r
}