swf2ass-go/types/shapes/Shape.go

222 lines
4.9 KiB
Go

package shapes
import (
"fmt"
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/math"
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/records"
)
type Shape []records.Record
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))
}
return newShape
}
func (s Shape) Start() math.Vector2[float64] {
if len(s) == 0 {
return math.NewVector2[float64](0, 0)
}
return s[0].GetStart()
}
func (s Shape) End() math.Vector2[float64] {
if len(s) == 0 {
return math.NewVector2[float64](0, 0)
}
return s[len(s)-1].GetEnd()
}
func (s Shape) IsFlat() bool {
for _, e := range s {
if !e.IsFlat() {
return false
}
}
return true
}
func (s Shape) IsClosed() bool {
return s.Start().Equals(s.End())
}
func (s Shape) Reverse() (r Shape) {
r = make(Shape, len(s))
for i, e := range s {
r[len(s)-1-i] = e.Reverse()
}
return r
}
func (s Shape) Merge(o Shape) (r Shape) {
r = make(Shape, 0, len(s)+len(o))
r = append(r, s...)
r = append(r, o...)
return r
}
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
func (s Shape) Flatten() (r Shape) {
if s.IsFlat() {
return s
}
r = make(Shape, 0, len(s)*4)
for _, e := range s {
r = append(r, records.FlattenRecord(e, 1)...)
}
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
}
func (s Shape) Equals(o Shape) bool {
if len(s) != len(o) {
return false
}
for i := range s {
if !s[i].Equals(o[i]) {
return false
}
}
return true
}
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
}
func IterateMorphShape(start, end Shape) (r []records.RecordPair) {
var prevStart, prevEnd records.Record
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
startEdge = records.MoveRecord{
To: startEdge.GetStart(),
Start: prevStart.GetEnd(),
}
}
if prevEnd != nil && !prevEnd.GetEnd().Equals(endEdge.GetStart()) {
advanceEnd = false
endEdge = records.MoveRecord{
To: endEdge.GetStart(),
Start: prevEnd.GetEnd(),
}
}
if startEdge.SameType(endEdge) {
r = append(r, records.RecordPair{startEdge, endEdge})
} else {
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 {
endEdge = records.MoveRecord{
To: endEdge.GetStart(),
Start: endEdge.GetStart(),
}
r = append(r, records.RecordPair{aMoveRecord, endEdge})
advanceEnd = false
} else if !aIsMoveRecord && bIsMoveRecord {
startEdge = records.MoveRecord{
To: startEdge.GetStart(),
Start: startEdge.GetStart(),
}
r = append(r, records.RecordPair{startEdge, bMoveRecord})
advanceStart = false
} else {
panic("incompatible")
}
}
if advanceStart {
start = start[1:]
}
if advanceEnd {
end = end[1:]
}
prevStart = startEdge
prevEnd = endEdge
}
if len(start) != 0 || len(end) != 0 {
panic("incompatible result")
}
return r
}