func (m *Model) FindObject(path string, id uint32) (*Object, bool) {
	if rs, ok := m.FindResources(path); ok {
		return rs.FindObject(id)
	}
	return nil, false
}
FindObject returns the object with the target path and ID.
package go3mf
import (
	"encoding/xml"
	"image/color"
	"io"
	"sort"
	"sync"
	"github.com/hpinc/go3mf/spec"
)
const (
	Namespace = "http://schemas.microsoft.com/3dmanufacturing/core/2015/02"
	RelType3DModel = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"
	RelTypeThumbnail = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"
	RelTypePrintTicket = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket"
	RelTypeMustPreserve = "http://schemas.openxmlformats.org/package/2006/relationships/mustpreserve"
	DefaultModelPath = "/3D/3dmodel.model"
	DefaultPrintTicketName = "/3D/Metadata/Model_PT.xml"
	Default3DTexturesDir = "/3D/Textures/"
	Default3DOtherDir = "/3D/Other/"
	DefaultMetadataDir = "/Metadata/"
	ContentType3DModel = "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"
	ContentTypePrintTicket = "application/vnd.ms-printing.printticket+xml"
)
type Units uint8
const (
	UnitMillimeter Units = iota
	UnitMicrometer
	UnitCentimeter
	UnitInch
	UnitFoot
	UnitMeter
)
func (u Units) String() string {
	return map[Units]string{
		UnitMillimeter: "millimeter",
		UnitMicrometer: "micron",
		UnitCentimeter: "centimeter",
		UnitInch:       "inch",
		UnitFoot:       "foot",
		UnitMeter:      "meter",
	}[u]
}
type ObjectType int8
const (
	ObjectTypeModel ObjectType = iota
	ObjectTypeOther
	ObjectTypeSupport
	ObjectTypeSolidSupport
	ObjectTypeSurface
)
func (o ObjectType) String() string {
	return map[ObjectType]string{
		ObjectTypeModel:        "model",
		ObjectTypeOther:        "other",
		ObjectTypeSupport:      "support",
		ObjectTypeSolidSupport: "solidsupport",
		ObjectTypeSurface:      "surface",
	}[o]
}
type Asset interface {
	XMLName() xml.Name
	Identify() uint32
}
type Metadata struct {
	Name     xml.Name
	Value    string
	Type     string
	Preserve bool
}
type Attachment struct {
	Stream      io.Reader
	Path        string
	ContentType string
}
type Relationship struct {
	Path string
	Type string
	ID   string
}
type Build struct {
	Items   []*Item
	AnyAttr spec.AnyAttr
}
type Resources struct {
	Assets  []Asset
	Objects []*Object
	AnyAttr spec.AnyAttr
}
func (rs *Resources) UnusedID() uint32 {
	if len(rs.Assets) == 0 && len(rs.Objects) == 0 {
		return 1
	}
	ids := make([]int, len(rs.Assets)+len(rs.Objects)+1)
	ids[0] = 0
	for i, r := range rs.Assets {
		id := r.Identify()
		ids[i+1] = int(id)
	}
	for i, o := range rs.Objects {
		ids[len(rs.Assets)+i+1] = int(o.ID)
	}
	sort.Ints(ids)
	lowest := 0
	for i, id := range ids {
		if id != i {
			lowest = i
			break
		}
	}
	if lowest == 0 {
		lowest = ids[len(ids)-1] + 1
	}
	return uint32(lowest)
}
func (rs *Resources) FindObject(id uint32) (*Object, bool) {
	for _, value := range rs.Objects {
		if value.ID == id {
			return value, true
		}
	}
	return nil, false
}
func (rs *Resources) FindAsset(id uint32) (Asset, bool) {
	for _, value := range rs.Assets {
		if rID := value.Identify(); rID == id {
			return value, true
		}
	}
	return nil, false
}
type Extension struct {
	Namespace  string
	LocalName  string
	IsRequired bool
}
type ChildModel struct {
	Resources     Resources
	Relationships []Relationship
	Any           spec.Any
}
type Model struct {
	Path              string
	Language          string
	Units             Units
	Thumbnail         string
	Resources         Resources
	Build             Build
	Attachments       []Attachment
	Extensions        []Extension  
	Metadata          []Metadata
	Childs            map[string]*ChildModel  
	RootRelationships []Relationship
	Relationships     []Relationship
	Any               spec.Any
	AnyAttr           spec.AnyAttr
}
func (m *Model) PathOrDefault() string {
	if m.Path == "" {
		return DefaultModelPath
	}
	return m.Path
}
func (m *Model) BoundingBox() Box {
	if len(m.Build.Items) == 0 {
		return Box{}
	}
	var wg sync.WaitGroup
	var mu sync.Mutex
	box := newLimitBox()
	wg.Add(len(m.Build.Items))
	for i := range m.Build.Items {
		go func(i int) {
			defer wg.Done()
			item := m.Build.Items[i]
			if o, ok := m.FindObject(item.ObjectPath(), item.ObjectID); ok {
				ibox := o.boundingBox(m, "")
				if ibox != emptyBox {
					mu.Lock()
					box = box.extend(item.Transform.MulBox(ibox))
					mu.Unlock()
				}
			}
		}(i)
	}
	wg.Wait()
	return box
}
func (m *Model) FindResources(path string) (*Resources, bool) {
	if path == "" || path == m.Path || (m.Path == "" && path == DefaultModelPath) {
		return &m.Resources, true
	}
	if child, ok := m.Childs[path]; ok {
		return &child.Resources, true
	}
	return nil, false
}
func (m *Model) FindAsset(path string, id uint32) (Asset, bool) {
	if rs, ok := m.FindResources(path); ok {
		return rs.FindAsset(id)
	}
	return nil, false
}