Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,18 +310,16 @@ func passThroughImpl(pp *pc.PointCloud, core func(_, _ *pc.PointCloud) int) (*pc
}

func (e *editor) merge(pp *pc.PointCloud) error {
pcNew := &pc.PointCloud{
PointCloudHeader: e.pp.PointCloudHeader.Clone(),
Points: e.pp.Points + pp.Points,
Data: append(e.pp.Data[:e.pp.Stride()*e.pp.Points], pp.Data...),
}
pcNew.Width = pcNew.Points
pcNew.Height = 1

if err := e.push(newUndoDataEntireCloud(e.pp)); err != nil {
if err := e.push(&undoDataSize{
Points: e.pp.Points,
Width: e.pp.Width,
Height: e.pp.Height,
}); err != nil {
return err
}
e.pp = pcNew
n := e.pp.Points + pp.Points
data := append(e.pp.Data[:e.pp.Stride()*e.pp.Points], pp.Data...)
e.pp = newCloudView(e.pp, n, n, 1, data)
runtime.GC()
return nil
}
33 changes: 33 additions & 0 deletions record.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ type undoData interface {
func init() {
gob.Register(&undoDataEntireCloud{})
gob.Register(&undoDataLabels{})
gob.Register(&undoDataSize{})
}

// pcgol caches an unsafe float32 alias of Data keyed only by its base pointer,
// so a change of the Data length must be delivered in a fresh PointCloud.
func newCloudView(pp *pc.PointCloud, points, width, height int, data []byte) *pc.PointCloud {
out := &pc.PointCloud{
PointCloudHeader: pp.PointCloudHeader.Clone(),
Points: points,
Data: data,
}
out.Width = width
out.Height = height
return out
}

var (
Expand Down Expand Up @@ -97,6 +111,25 @@ func (p *undoDataLabels) payload() []byte {

func (p *undoDataLabels) setPayload([]byte) {}

type undoDataSize struct {
Points, Width, Height int
}

func (p *undoDataSize) restore(pp *pc.PointCloud) (*pc.PointCloud, error) {
stride := pp.Stride()
if stride <= 0 || p.Points < 0 || p.Width < 0 || p.Height < 0 ||
p.Points > pp.Points || p.Points > len(pp.Data)/stride {
return nil, errBrokenRecord
}
return newCloudView(pp, p.Points, p.Width, p.Height, pp.Data[:p.Points*stride]), nil
}

func (p *undoDataSize) payload() []byte {
return nil
}

func (p *undoDataSize) setPayload([]byte) {}

// A record is this encoding followed by the raw payload.
func encodeUndoData(w io.Writer, d undoData) error {
return gob.NewEncoder(w).Encode(&d)
Expand Down
19 changes: 19 additions & 0 deletions record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ func TestUndoDataLabelsRestore(t *testing.T) {
assertCloudEqual(t, orig, out)
}

func TestUndoDataSizeRestore(t *testing.T) {
orig := makeTestCloud(t, 100, 10, 10)
pp := cloneCloud(orig)

p := &undoDataSize{Points: pp.Points, Width: pp.Width, Height: pp.Height}
added := makeTestCloud(t, 10, 10, 1)
pp.Data = append(pp.Data, added.Data...)
pp.Points += added.Points
pp.Width = pp.Points
pp.Height = 1

out, err := p.restore(pp)
if err != nil {
t.Fatal(err)
}
assertCloudEqual(t, orig, out)
}

func TestUndoDataEntireCloudRestore(t *testing.T) {
orig := makeTestCloud(t, 100, 10, 10)
pp := makeTestCloud(t, 5, 5, 1)
Expand All @@ -95,6 +113,7 @@ func TestRecordEncodeDecodeRoundTrip(t *testing.T) {
for name, d := range map[string]undoData{
"EntireCloud": newUndoDataEntireCloud(orig),
"Labels": &undoDataLabels{Indices: []uint32{1, 2, 42}, OldLabels: []uint32{7, 8, 9}},
"Size": &undoDataSize{Points: 90, Width: 9, Height: 10},
} {
t.Run(name, func(t *testing.T) {
var buf bytes.Buffer
Expand Down
Loading