Copying Objects

From DocR23

Jump to: navigation, search

This article discusses the terminology and behavior of the ACIS copy functionality.

Contents

Copying Objects

ACIS provides class copy constructors, class methods, and functions for copying objects. A copy of an object may be a deep copy or a shallow copy.

A deep copy is a copy made by replicating the object plus any assets owned by the object, including objects pointed at by data members of the object being copied. The copied item does not share any data with the original. A deep copy allocates new storage for all member data and any pointers, so that all the information about that item is self-contained in its own memory block.

A shallow copy copies an object, but instead of copying all the other objects it references (a deep copy) it references the same objects that the original uses. A shallow copy stores only the first instance of the item in memory, and increments a use count for each copy.

A deep copy should be used to create a copy of an object that does not share any underlying information with the original object. One reason for using a deep copy of an entity is to move the deep copy into a different history stream, breaking all ties with the previous history stream. There are some entities that cannot be deep copied (by design). If such entities are present during a deep copy, a sys_error will be thrown. The API functions that perform a deep copy have a logical argument to specify if attributes that cannot be deep copied need to be skipped over when doing a deep copy. Any non-attribute entities that cannot be deep copied will throw a sys_error regardless of the value of the logical argument.

Types of Copies

This section details the various ACIS copy behaviors.

Shallow Copy

APIs: api_copy_entity, api_copy_entity_list

The goal of the ACIS shallow copy mechanism is to create an "up-and-down" copy of the input entity(s). "Up-and-down" is with respect to the topology of the entity. For example, looking "up" from a SHELL are the topological entities LUMP and BODY. Accordingly, looking "down" from a COEDGE are the topological entities EDGE and VERTEX. Thus, if you pass a FACE as input to api_copy_entity, every reachable part of the topology above and below the FACE is copied, ultimately resulting in a full BODY copy.

The shallow copy process creates a copy of every scanned entity. Although geometric entities (such as SURFACE, CURVE, PCURVE, and APOINT) are designed to have multiple owners, the shallow copy process does not utilize this use counting mechanism. Instead, geometric data sharing occurs at a much lower level. Geometric entities contain an instance of a (lowercase) construction geometry object. The definition of some construction geometry objects (in particular, spline, intcurve, and pcurve objects) are based upon classes that are use-counted. With a shallow copy, these low-level classes may be multiply referenced, which facilitates data sharing between the original and copied geometric objects. This sharing of geometric data between the original BODY and copied BODY might be problematic if you intend to create a copy of an entity on a different history stream, because ACIS supports having various entities that make up a BODY be across different or multiple history streams. (Refer to Deep Copy for details on how it helps solve the problem of creating a copy of an entity that can be moved to a new history stream.)

Attributes (those entities derived from the ATTRIB class) can also participate in the shallow copy process. If the design intent of the attribute is to be copied when its owner is copied, then the attribute should implement its copyable() method to return TRUE.

Note: Attributes that do not implement copyable() are copyable by default.


Finally, the attribute implements its SCAN_DEF, COPY_DEF/COPY_WITH_DEEP_COPY_DEF, and FIX_POINTER_DEF entity derivation macros. Refer to the section Copy-Related Entity Derivation Macros for a detailed discussion about these macros.

Deep Copy

APIs: api_deep_copy_entity, api_deep_copy_entity_list

The ACIS deep copy mechanism is also an "up-and-down" copy similar to shallow copy, but it also makes copies of all the geometric data defining the geometric entities participating in the copy. Use this mechanism when you want to create a copy of an entity on a different history stream.

The only geometric objects that have special-purpose logic distinguishing between a shallow and a deep copy are the low level spl_sur, int_cur, and par_cur classes. Their design intent is to add new owners during a shallow copy, and to be copied during a deep copy. Although there are other multiple-owned (use-counted) objects in ACIS, none are utilized during the copy-scan process.

Down Copy

APIs: api_down_copy_entity

In contrast to the shallow and deep copy algorithms, the goal of the ACIS down copy mechanism is not to create an "up-and-down" copy, but rather, just a "down" copy of the input entity. Like a shallow copy, a down copy may share geometric data between the original and the copied objects.

For example, if a FACE is down copied, all of its LOOPs, COEDGEs, EDGEs, and VERTEXes are copied. The copied FACE is returned.

In contrast to the shallow and deep copy process, attributes have a different type of involvement with the down copy process. Attributes are not directly copied as part of the down copy process. Instead ACIS notifies the attributes that their owner has been copied - after the down copy is complete. The notification is sent via the attribute's copy_owner method. copy_owner is called regardless of whether the attribute is copyable.

The fundamental reason for this delayed-notification approach is due to complex attributes (attributes that store pointers to entities other than their owner). Complex attributes do not have a fixed location in the topological hierarchy and do not have the concept of what entities are "above" or "below" them.

For example, consider a complex attribute on a FACE that contains a BODY pointer as member data, and that the attribute's BODY pointer points to the FACE's owning BODY. If that FACE is down-copied, what should happen to the attribute? There are many choices as to what might be considered "correct" behavior. The attribute might choose to not be copied, or it might choose to be copied with a pointer to the original FACE's owning BODY, or it might choose to be copied with a pointer to a NULL BODY, or the attribute might even lose itself. Whatever the decision, the attribute makes the decision in its copy_owner method.

Deep Down Copy

APIs: api_deep_down_copy_entity

The deep down copy process is similar to the down copy process, except that all the geometric data underlying the geometric entities are copied, as in a deep copy. Use this mechanism when you want to create a down-copy of an entity on a different history stream.

Unlike the "up-and-down" copy process, during the "down" copy process, attributes are not automatically copied (via their copy_data method). Instead, attributes are notified that their owner was copied using their copy_owner method. This is a required and intentional difference of behavior: attributes are not part of the ACIS topology and therefore do not know how to be "down-copied." Granted, simple attributes could be automatically included in a down-copy; it is complex attributes (attributes that point at entities other than their owner) that are unable to be down-copied.

Comparison of Copy Mechanisms

The following table summarizes the capabilities of the four copy mechanisms.

Comparison of ACIS Copy Mechanisms
Shallow Copy Deep Copy Down Copy Deep Down Copy
Scan Type Up-and-down Up-and-down Down-only Down-only
Data Sharing between Copy and Original Yes No Yes No
Attribute Copy Mechanism Copy-Scan Process Copy-Scan Process copy_owner method copy_owner method

To illustrate the difference between shallow and deep copying, consider a BODY that contains two INTCURVEs, each of which contains an intcurve that references the same int_cur. That is, one int_cur serves as the geometric definition of both INTCURVEs. (Such a BODY can be constructed by creating a "wiggle" and splitting one of its B-spline edges. In this example, we are interested only in the geometry of two resulting edges.)

If the BODY is shallow copied, the new BODY will contain two new INTCURVEs, each of which contains an intcurve that references the original int_cur. That is, one int_cur serves as the geometric definition for all four INTCURVEs.

Alternatively, if the BODY is deep copied, the new BODY will contain two new INTCURVEs, each of which contains an intcurve that references one new int_cur. That is, one int_cur serves as the geometric definition for both INTCURVEs in the original BODY and one int_cur serves as the geometric definition for both INTCURVEs in the new BODY. The int_curs in the new BODY may be multiply referenced (that is, they may have a use count greater than one) to save memory and make algorithms more efficient, but they are not shared by the original BODY.

The Copy Process

The following steps are common to all types of ACIS copy processes (shallow, deep, down, and deep down):

Step 1: Scan
Step 2: Copy
Step 3: Fix-pointers

The down copy process includes this additional step:

Step 4: Notify

These steps are discussed immediately below.

Step 1: Scan

All copy algorithms start with either a single entity or a list of entities to be copied. Given the input entity(s), the first step of the copy process includes scanning the input entity(s), thereby building a list of entities to be copied.

The scanning process queries an entity in order to determine the list of entities on which it depends. For example, when a LUMP is scanned for the purposes of a shallow copy, the LUMP adds its owning BODY, the next LUMP, its SHELL, and possibly its first ATTRIB. (In a shallow copy, attributes are added to the list only if they are "copyable".)

The scan process generates a list of all entities that are involved in the copy (depending on the style of copy at hand). Therefore, every input entity is scanned, and every entity on which the input entities depend, and so on. This iterative process of scan/add/scan/add/ and so forth, continues until all entities to be involved in the copy are scanned.

The following short sample generates a list of entities that are involved in a shallow copy of a given input_ent:

ENTITY * ent = NULL;
 ENTITY_LIST shallow_copy_list;
 shallow_copy_list.add( input_ent );
 shallow_copy_list.init();
 while ( ent = shallow_copy_list.next() )
 	ent.copy_scan( shallow_copy_list, SCAN_COPY );

Note: The above sample employs the copy_scan method which drives the SCAN_DEF entity derivation macro. Refer to the section Copy-Related Entity Derivation Macros for more details about this macro.


Step 2: Copy

Previously, in Step 1: Scan, you built a list of entities to be copied by scanning them. Once the scan-list has been generated, the actual copying begins.

In Step 2: Copy, you allocate storage for an array of copied entities. This array is referred to as the copy-list array, or simply "copy-list". Copy each entity from the scan-list and place it in the copy-list. In summary, for each entity in the scan-list:

  1. Construct a new entity of the same type.
  2. Copy the source entity's data into the new entity.
  3. Store the new entity in the copy-list array.

Part of the challenge in (2), however, is copying entity pointers. Using the same entity pointers as the source breaks the topology linkage. Ideally, the new entity pointer member data needs to point to the respective copied entities in the copy-list. Unfortunately, because you copy each entity in order, the entities on which the entity being copied depends upon may not yet be copied.

However, the index contains where dependent entity pointers will eventually reside in the copy-list. Therefore, instead of copying entity pointers into each entity's member data, you can, instead, store the index into the copy-list where the dependent entity will eventually reside.

Converting the entity indices into actual entity pointers takes place in Step 3: Fix-pointers.

The entity methods that accomplish Step 2: Copy are:

  • copy_data
  • copy_common

These methods are related to the ENTITY_DEF (or ATTRIB_DEF for attributes) and COPY_DEF/COPY_WITH_DEEP_COPY_DEF entity derivation macros, respectively. Refer to the section Copy-Related Entity Derivation Macros for more information about these macros.

Step 3: Fix-pointers

At this point, you have accomplished the following:

  • Scanned the input entity(s) to create the scan-list.
  • Copied every entity in the scan-list and stored them in the copy-list.

However, each entity's entity pointer member data in the copy-list actually stores an index into the copy-list. Because the copy-list has now become fully populated with valid copies of entities, Step 3: Fix-pointers can now complete the copy process by having each entity map the entity index that was copied into a real pointer by simply indexing into the copy-list. This method is related to the FIX_POINTER_DEF entity derivation macro. Refer to the section Copy-Related Entity Derivation Macros for more information about these macros.

Step 4: Notify

Note: This step applies to the down copy process only.


As mentioned earlier, the attributes' involvement differs with the down copy process; that is, the previous steps exclude the attributes involvement. Instead, after completion of the previous steps, you notify each attribute on the source entity that its owner was copied via its copy_owner method. Refer to Attributes for more guidance on implementing the copy_owner method.

Copy-Related Entity Derivation Macros

When you derive from ENTITY or ATTRIB to create your own entity or attribute, Spatial recommends that you use the Entity Derivation Macros or Attribute Derivation Macros. The entity and attribute derivation macros encapsulate and simplify the implementation of new entities and attributes. Depending on the level of sophistication of your derived entity/attribute, you may implement up to ten of these macros. Four of the entity/attribute derivation macros you implement are related to the copy process:

  • ENTITY_DEF/ATTRIB_DEF (for entities/attributes, respectively)
  • SCAN_DEF
  • COPY_DEF/COPY_WITH_DEEP_COPY_DEF
  • FIX_POINTER_DEF

The following section demonstrates how to implement these entity derivation macros for a fictional attribute. The design intent of this fictional attribute is that it be both copyable and deep-copyable. This attribute stores as member data a SPAbox m_box, and a pointer to a custom entity PROPS * m_props.

Note: Implementing a complex attribute such as this is non-trivial for many reasons (such as saving/restoring, rolling with history) outside of the copy process. The following example focuses on the copy aspects of our fictional complex attribute.


The discussion includes the copy-related entity derivation macros in the order the copy process uses them (which parallels the section The Copy Process).

Step 1: SCAN_DEF

Entities implement the SCAN_DEF macro to specify how they should be scanned. Behind the SCAN_DEF macro, you implement your entity's copy_scan method:

virtual void copy_scan(   ENTITY_LIST & list,
                           SCAN_TYPE reason = SCAN_COPY,
                           logical dpcpy_skip = FALSE) const;

ENTITY_LIST & list

The list of entities to be built during this step. Add entities to this list that you want included in the scan/copy/fix-up process.

SCAN_TYPE reason

The reason your entity is being scanned. Types of reasons relevant to the different styles of copy include:
  • SCAN_COPY: shallow copy
  • SCAN_DEEP_COPY: deep copy
  • SCAN_DOWN: down copy

Note: There are other scan reasons that are not copy-related.


logical dpcpy_skip

Used only by the base ENTITY and base ATTRIB class. No other derived entity or attribute utilizes this value. This logical indicates how non-deepcopyable attributes are handled during a deep copy. If TRUE, the copy continues even if it encounters attributes marked as not deep-copyable. By default (FALSE), if any attributes found to be not deep-copyable, a sys_error is thrown.

Note: This flag affects only how attributes are handled; any non-deep-copyable, non-attribute entities encountered cause a sys_error to be thrown regardless of this flag's setting.


For our fictional custom attribute, its SCAN_DEF implementation would look as follows:

SCAN_DEF
        switch (reason)
        {
               case SCAN_COPY:
               case SCAN_DEEP_COPY:
                       list.add( m_props );
                       break;
 
               // add other non-copy scan cases here...
        }

Our goal in implementing the above scan-def is to add your m_props entity to the scan-list. This ensures that m_props participates in the copy process.

Important notes about SCAN_DEF include:

  • If an attribute returns copyable()== FALSE, then the attribute is not scanned for the purposes of shallow copy (SCAN_COPY) or deep copy (SCAN_DEEP_COPY). Therefore, the attribute need not implement any special case logic for these reasons in its SCAN_DEF.
  • Attributes are never included in a SCAN_DOWN; therefore, an attribute need not implement any special case logic for SCAN_DOWN.

Step 2: ENTITY_DEF/ATTRIB_DEF and COPY_DEF/COPY_WITH_DEEP_COPY_DEF

To perform a copy, two entity derivation macros are needed: either ENTITY_DEF or ATTRIB_DEF, and either COPY_DEF or COPY_WITH_DEEP_COPY_DEF. Use ENTITY_DEF when deriving non-attribute entities; use ATTRIB_DEF when defining attributes.

Both COPY_DEF and COPY_WITH_DEEP_COPY_DEF describe how to copy your entity/attribute. When deriving, you must choose to implement one of these macros, but not both. Choose COPY_DEF to indicate that your entity/attribute is meant to be only shallow copied, not deep copied. Choose COPY_WITH_DEEP_COPY_DEF to indicate that your entity/attribute may be either shallow or deep copied.

Behind the ENTITY_DEF and ATTRIB_DEF macros is the copy_data method for allocating and copying a new entity.

virtual ENTITY* copy_data(  ENTITY_LIST & list,
                             pointer_map *pm = NULL,
                             logical dpcpy_skip = FALSE,
                             SCAN_TYPE reason = SCAN_COPY ) const;

copy_data is implemented using the ENTITY_DEF or ATTRIB_DEF macros. It allocates storage for the desired entity (using ACIS_NEW), then invokes a call to the copy_common method.

void copy_common(  ENTITY_LIST & list,
                    const ENTITY * from,
                    pointer_map *pm = NULL,
                    logical dpcpy_skip = FALSE,
                    SCAN_TYPE reason = SCAN_COPY );

copy_common is the method underlying the COPY_DEF and COPY_WITH_DEEP_COPY_DEF macros. The copy_data and copy_common methods have very similar arguments. They are described as follows:

ENTITY_LIST & list

The list of entities determined during the scan process, referred to as the scan-list. These are the entities involved in the copy. This list is meant for query purposes only.

const ENTITY * from

The newly constructed entity created in copy_data being passed into copy_common.

pointer_map *pm

Relevant if your entity has multiple owners, like ACIS curves and surfaces. The pointer map allows the implementer to add-to and/or query a list of already copied multiply-owned entities.

logical dpcpy_skip

Used only by the base ENTITY and base ATTRIB class. No other derived entity or attribute should need this value. This logical indicates how non-deepcopyable attributes are handled during a deep copy. If TRUE, the copy continues even if it encounters attributes marked as not deep-copyable. By default (FALSE), if any attributes found to be not deep-copyable, a sys_error is thrown. (This flag only affects how attributes are handled; any non-deepcopyable, non-attribute entities encountered cause a sys_error to be thrown regardless of this flag's setting.)

SCAN_TYPE reason

The scan reason used to create the ENTITY_LIST & list. In some circumstances, knowing the scan reason during the copy process may be useful.

Your objective in implementing COPY_DEF/COPY_WITH_DEEP_COPY_DEF is to describe how to copy the from entity's data onto this entity.

For the fictional custom attribute, its COPY_WITH_DEEP_COPY_DEF implementation would look as follows:

COPY_WITH_DEEP_COPY_DEF 
     m_box = from->m_box; 
     m_props = ( PROPS *) INTEXTEND list.lookup( from->m_props );

When copying m_props, you are not actually copying a pointer value; you are storing an index into the m_props pointer. This is the index for the from entity as it is represented in list (the list generated by scanning). In other words, if the from entity is the 17th entry in the scan-list array, then after the assignment, m_props will have a value of 17. To convert this index back into a pointer, we rely on the final step of the copy process: fix-up pointers.

Step 3: FIX_POINTER_DEF

The last step of the copy process, fix-up pointers, relies on the FIX_POINTER_DEF macro. FIX_POINTER_DEF serves a dual purpose: it is used both by the copy process and the restore process. Whatever the process, the purpose of the fix-up process is the same: to convert entity pointer member data from indices to pointers.

Behind the FIX_POINTER_DEF macro is the fix_common entity method for fixing-up pointers:

void fix_common(  ENTITY * array,
                   SCAN_TYPE reason = SCAN_COPY );

ENTITY * array

The array of copied entities. Has the same number of entities and is in the same order as the scan-list. To resolve indices into pointers, this array is indexed.

SCAN_TYPE reason

The scan reason used to create the scan-list. In some circumstances, knowing the scan reason during the fix-up process may be useful.

Returning to the fictional attribute's implementation:

FIX_POINTER_DEF
     m_props = (PROPS *) read_array( array, m_props );

Using the global read_array function resolves the m_props pointer value. Note that after the COPY_DEF, m_props contains an index. Pass that value to read_array as the second argument. read_array then indexes the array at this index and returns the desired entity pointer.

Summary

Types of methods to copy an entity include:

  • shallow
  • deep
  • down
  • deep down

When copying entity(s), choose the copy API that fits what you want to accomplish:

  • api_copy_entity( _list): shallow up-and-down copy using SCAN_COPY
  • api_deep_copy_entity(_list): deep up-and-down copy using SCAN_DEEP_COPY
  • api_down_copy_entity: shallow down copy using SCAN_DOWN
  • api_deep_down_copy_entity: deep down copy using a modified SCAN_DOWN

When implementing your own entity/attribute, consider how you want your entity to behave.

  • Will it be copyable?
  • Deep-copyable?
  • Savable?
  • Are you implementing a complex attribute?
  • How will it behave when it receives a copy_owner notification (from a down copy)?

Knowing the answers to these questions and how the copy process works will help you implement the entity derivation macros related to the copy process: SCAN_DEF, COPY_DEF/COPY_WITH_DEEP_COPY_DEF, and FIX_POINTER_DEF.

Deprecated Copy Functionality: api_copy_entity_contents

Spatial does not recommend the usage of api_copy_entity_contents; as of R16, this function has been deprecated. The API has been replaced by api_down_copy_entity.

The API api_copy_entity_contents has been deprecated because it did not follow the "standard" ACIS copy process. Many easily created scenarios using complex attributes cause api_copy_entity_contents to create an invalid model.

The API api_down_copy_entity follows the "standard" ACIS copy process and the rules therein, as described in Down Copy.

Personal tools