222 lines
4.9 KiB
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
|
|
}
|