|
|
|
@ -32,17 +32,109 @@ db.AppendEntity(ent);
|
|
|
|
|
当我们删除对象时,尝试只对对象进行标记删除.
|
|
|
|
|
当历史记录被优化时,我们尝试对删除的对象进行彻底删除
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
###关于对象的历史记录信息
|
|
|
|
|
|
|
|
|
|
->对象创建
|
|
|
|
|
....
|
|
|
|
|
undo:删除该对象,在集合中清除该数据 Remove
|
|
|
|
|
redo:重新创建该对象.保存完整的数据 A
|
|
|
|
|
|
|
|
|
|
->对象删除
|
|
|
|
|
undo:添加删除标记. P
|
|
|
|
|
redo:取消删除标记. P
|
|
|
|
|
|
|
|
|
|
->对象修改
|
|
|
|
|
undo:添加补丁. P
|
|
|
|
|
redo:添加补丁 P
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
问题:对象完整性保存时,优化重复的保存对象状态(不同命令下)
|
|
|
|
|
|
|
|
|
|
R:补丁与完整的对象存储不能优化.
|
|
|
|
|
假设用A表示完整的对象补丁,P表示局部补丁. 那么以下状态无法被优化.
|
|
|
|
|
如 A P P P A P P
|
|
|
|
|
| 这里A部分无法被优化 必须保证拥有旧的和新的全部数据.
|
|
|
|
|
假设拥有连续的完整补丁.
|
|
|
|
|
如 A A A A
|
|
|
|
|
连续的A是可以优化旧数据的.
|
|
|
|
|
|
|
|
|
|
结论:不优化.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
问题:对象历史记录,完整性保存时优化.(相同命令下)
|
|
|
|
|
当对象已经尝试完整的保存一个对象时,如果还继续往里面保存一个完整的数据,那么将更新旧的完整数据.
|
|
|
|
|
比如.
|
|
|
|
|
A (insert new A).
|
|
|
|
|
A (update A.redo)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
历史记录的数据结构
|
|
|
|
|
|
|
|
|
|
cmdName:"Line" //对象的创建命令
|
|
|
|
|
[
|
|
|
|
|
Object1:[C]//对象保留的创建的记录
|
|
|
|
|
]
|
|
|
|
|
cmdName:"Move" //对象修改命令
|
|
|
|
|
[
|
|
|
|
|
Object1:[P],
|
|
|
|
|
ObjectN:[P],
|
|
|
|
|
ObjectM:[P],
|
|
|
|
|
...
|
|
|
|
|
]
|
|
|
|
|
cmdName:"Ohther" //假设这个是一个复杂度很高的命令
|
|
|
|
|
[
|
|
|
|
|
Object1:[A,P,A,P,A,P,P,P],保存连续的记录.
|
|
|
|
|
ObjectN:[A,P,A,P,A,P,P,P],
|
|
|
|
|
ObjectM:[C,P,P,P,P] ,假设C为创建数据
|
|
|
|
|
...
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
问题:对象id的更新.
|
|
|
|
|
跨文档复制时,对象的id和引用的id应该被更新.
|
|
|
|
|
加入ObjectId类.
|
|
|
|
|
id只有在一个情况下才会更新.跨文档复制时.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//CAD对象工厂,通过注册 和暴露的创建方法,动态创建对象
|
|
|
|
|
class CADFactory
|
|
|
|
|
{
|
|
|
|
|
private constructor() { }
|
|
|
|
|
private objectNameMap = new Map<string, any>();
|
|
|
|
|
private static factory = new CADFactory();
|
|
|
|
|
static RegisterObject(C)
|
|
|
|
|
{
|
|
|
|
|
let obj = new C();
|
|
|
|
|
this.factory.objectNameMap.set(obj.ClassName.toUpperCase(), () => new C());
|
|
|
|
|
}
|
|
|
|
|
static CreateObject(name: string): CADObject
|
|
|
|
|
{
|
|
|
|
|
let createF = this.factory.objectNameMap.get(name.toUpperCase());
|
|
|
|
|
if (createF)
|
|
|
|
|
return createF();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//所有cad对象的基类
|
|
|
|
|
class CADObject
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
protected db: Database;
|
|
|
|
|
//对象是否已经被删除
|
|
|
|
|
private isErase: boolean = false;
|
|
|
|
|
protected isErase: boolean = false;
|
|
|
|
|
protected objectId: ObjectId;
|
|
|
|
|
|
|
|
|
|
get Id(): ObjectId
|
|
|
|
|
{
|
|
|
|
|
return this.objectId;
|
|
|
|
|
}
|
|
|
|
|
get IsErase(): boolean
|
|
|
|
|
{
|
|
|
|
|
return this.isErase;
|
|
|
|
@ -51,6 +143,12 @@ class CADObject
|
|
|
|
|
//-----------------------------File-----------------------------
|
|
|
|
|
//对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化
|
|
|
|
|
|
|
|
|
|
//类名,保证序列化时得到正确的new
|
|
|
|
|
get ClassName(): string
|
|
|
|
|
{
|
|
|
|
|
return "CADObject";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//对象从文件中读取数据,初始化自身
|
|
|
|
|
FileIn(file: CADFile)
|
|
|
|
|
{
|
|
|
|
@ -63,8 +161,15 @@ class CADObject
|
|
|
|
|
ApplyPartialUndo(file: CADFile)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------File End-----------------------------
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ObjectId
|
|
|
|
|
{
|
|
|
|
|
private id: number;
|
|
|
|
|
private constructor() { }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//cad文件数据
|
|
|
|
|
class CADFile
|
|
|
|
@ -77,86 +182,100 @@ class CommandHistoricRecord extends CADObject
|
|
|
|
|
//命令名称
|
|
|
|
|
commandName: string;
|
|
|
|
|
//历史记录表
|
|
|
|
|
historicCol: HistoricRecord[];
|
|
|
|
|
historicCol: Map<number, HistoricRecord[]>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//历史记录
|
|
|
|
|
class HistoricRecord
|
|
|
|
|
class HistoricRecord extends CADObject
|
|
|
|
|
{
|
|
|
|
|
objectId: number;
|
|
|
|
|
undoData: any;
|
|
|
|
|
redoData: any;
|
|
|
|
|
undoData: CADObject;
|
|
|
|
|
redoData: CADObject;
|
|
|
|
|
userData: CADObject;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HistoricManage
|
|
|
|
|
//历史记录管理
|
|
|
|
|
class HistoricManage extends CADObject
|
|
|
|
|
{
|
|
|
|
|
m_Db: Database;
|
|
|
|
|
curIndex: number = -1; //当前执行位置
|
|
|
|
|
historicRecord: CommandHistoricRecord[] = [];//历史记录
|
|
|
|
|
private curIndex: number = -1; //当前执行位置
|
|
|
|
|
private historicRecord: CommandHistoricRecord[] = [];//历史记录
|
|
|
|
|
|
|
|
|
|
constructor(db: Database)
|
|
|
|
|
{
|
|
|
|
|
super();
|
|
|
|
|
this.db = db;
|
|
|
|
|
}
|
|
|
|
|
get UndoData(): CommandHistoricRecord
|
|
|
|
|
{
|
|
|
|
|
if (this.historicRecord.length == 0)
|
|
|
|
|
if (this.historicRecord.length === 0)
|
|
|
|
|
{
|
|
|
|
|
this.startCmd("");
|
|
|
|
|
this.StartCmd("");
|
|
|
|
|
}
|
|
|
|
|
return this.historicRecord[this.historicRecord.length - 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startCmd(cmdName: string)
|
|
|
|
|
StartCmd(cmdName: string)
|
|
|
|
|
{
|
|
|
|
|
this.historicRecord.splice(this.curIndex, this.historicRecord.length - this.curIndex);
|
|
|
|
|
this.historicRecord.push(new CommandHistoricRecord());
|
|
|
|
|
this.curIndex = this.historicRecord.length - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Undo()
|
|
|
|
|
Undo(): boolean
|
|
|
|
|
{
|
|
|
|
|
let historicRec = this.historicRecord[this.curIndex];
|
|
|
|
|
if (!historicRec)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let recList = historicRec.historicCol;
|
|
|
|
|
for (let i = recList.length; i--;)
|
|
|
|
|
for (let [id, recList] of historicRec.historicCol)
|
|
|
|
|
{
|
|
|
|
|
let obj = this.db.getObject(id);
|
|
|
|
|
for (let rec of recList)
|
|
|
|
|
{
|
|
|
|
|
let data = recList[i];
|
|
|
|
|
let obj = this.m_Db.getObject(data.objectId);
|
|
|
|
|
obj.ApplyPartialUndo(data.undoData);
|
|
|
|
|
obj.ApplyPartialUndo(rec.undoData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.curIndex--;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
Redo(cout: number)
|
|
|
|
|
Redo()
|
|
|
|
|
{
|
|
|
|
|
let undoData = this.historicRecord[this.curIndex + 1];
|
|
|
|
|
if (!undoData)
|
|
|
|
|
let historicRec = this.historicRecord[this.curIndex + 1];
|
|
|
|
|
if (!historicRec)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (let rec of undoData.historicCol)
|
|
|
|
|
for (let [id, recList] of historicRec.historicCol)
|
|
|
|
|
{
|
|
|
|
|
let obj = this.m_Db.getObject(rec.objectId);
|
|
|
|
|
obj.ApplyPartialUndo(rec.redoData);
|
|
|
|
|
let obj = this.db.getObject(id);
|
|
|
|
|
for (let i = recList.length; i--;)
|
|
|
|
|
{
|
|
|
|
|
obj.ApplyPartialUndo(recList[i].redoData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.curIndex++;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Database extends CADObject
|
|
|
|
|
class Database
|
|
|
|
|
{
|
|
|
|
|
className: "Database";
|
|
|
|
|
hm: HistoricManage;
|
|
|
|
|
//块表记录
|
|
|
|
|
blockTableCol: BlockTableRecord[];
|
|
|
|
|
//材质字典
|
|
|
|
|
//模型空间
|
|
|
|
|
ModelSpace: BlockTableRecord = new BlockTableRecord();
|
|
|
|
|
//材质字典...
|
|
|
|
|
|
|
|
|
|
objectCol = new Map<number, CADObject>();
|
|
|
|
|
constructor()
|
|
|
|
|
{
|
|
|
|
|
super();
|
|
|
|
|
this.objectCol.set(0, this);
|
|
|
|
|
this.hm = new HistoricManage(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getObject(id: number): CADObject
|
|
|
|
@ -168,14 +287,6 @@ class Database extends CADObject
|
|
|
|
|
{
|
|
|
|
|
let id = this.objectCol.size;
|
|
|
|
|
this.objectCol.set(id, obj);
|
|
|
|
|
|
|
|
|
|
this.hm.UndoData.historicCol.push(
|
|
|
|
|
{
|
|
|
|
|
objectId: 0,
|
|
|
|
|
undoData: { type: 1, i: id },//remove
|
|
|
|
|
redoData: { type: 2, i: id },//add
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ApplyPartialUndo(data)
|
|
|
|
|
{
|
|
|
|
@ -192,32 +303,18 @@ class Database extends CADObject
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BlockTableRecord extends CADObject
|
|
|
|
|
{
|
|
|
|
|
objectMap = new Map<number, CADObject>();
|
|
|
|
|
private objectMap = new Map<number, CADObject>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum EntityUndoType
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
interface EntityData
|
|
|
|
|
{
|
|
|
|
|
id: number;
|
|
|
|
|
isErase: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* 所有图元的基类
|
|
|
|
|
*
|
|
|
|
|
* @class Entity
|
|
|
|
|
*/
|
|
|
|
|
//所有图元的基类
|
|
|
|
|
class Entity extends CADObject
|
|
|
|
|
{
|
|
|
|
|
m_Db: Database;
|
|
|
|
@ -228,89 +325,62 @@ class Entity extends CADObject
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 直线撤销类型
|
|
|
|
|
*
|
|
|
|
|
* @enum {number}
|
|
|
|
|
*/
|
|
|
|
|
enum LineUndoType
|
|
|
|
|
//直线撤销类型
|
|
|
|
|
export enum LineUndoType
|
|
|
|
|
{
|
|
|
|
|
Full = 0,
|
|
|
|
|
StartPoint = 1,
|
|
|
|
|
EndPoint = 2,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
interface LineData extends EntityData
|
|
|
|
|
{
|
|
|
|
|
startPoint: number[];
|
|
|
|
|
endPoint: number[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 直线对象
|
|
|
|
|
*
|
|
|
|
|
* @class Line
|
|
|
|
|
*/
|
|
|
|
|
//直线对象
|
|
|
|
|
class Line extends Entity
|
|
|
|
|
{
|
|
|
|
|
constructor()
|
|
|
|
|
{
|
|
|
|
|
super();
|
|
|
|
|
}
|
|
|
|
|
ApplyPartialUndo(data)
|
|
|
|
|
{
|
|
|
|
|
switch (data.type)
|
|
|
|
|
|
|
|
|
|
//-----------------------------File-----------------------------
|
|
|
|
|
//对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化
|
|
|
|
|
|
|
|
|
|
//类名,保证序列化时得到正确的new
|
|
|
|
|
get ClassName(): string
|
|
|
|
|
{
|
|
|
|
|
case LineUndoType.Full:
|
|
|
|
|
break;
|
|
|
|
|
case LineUndoType.StartPoint:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return "Line";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 图纸绘制适配器,使用适配器绘制对象图元.
|
|
|
|
|
*
|
|
|
|
|
* @class EentiyDrawAdapter
|
|
|
|
|
*/
|
|
|
|
|
class EentiyDrawAdapter
|
|
|
|
|
{
|
|
|
|
|
Draw()
|
|
|
|
|
//对象从文件中读取数据,初始化自身
|
|
|
|
|
FileIn(file: CADFile)
|
|
|
|
|
{
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 直线的绘制适配
|
|
|
|
|
*
|
|
|
|
|
* @class LineAdapter
|
|
|
|
|
* @extends {EentiyDrawAdapter}
|
|
|
|
|
*/
|
|
|
|
|
class LineAdapter extends EentiyDrawAdapter
|
|
|
|
|
{
|
|
|
|
|
Draw()
|
|
|
|
|
//对象将自身数据写入到文件.
|
|
|
|
|
FileOut(file: CADFile)
|
|
|
|
|
{
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Update(type: number)
|
|
|
|
|
ApplyPartialUndo(data)
|
|
|
|
|
{
|
|
|
|
|
switch (type)
|
|
|
|
|
switch (data.type)
|
|
|
|
|
{
|
|
|
|
|
case LineUndoType.Full:
|
|
|
|
|
break;
|
|
|
|
|
case LineUndoType.StartPoint:
|
|
|
|
|
break;
|
|
|
|
|
case LineUndoType.EndPoint:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//-----------------------------File End-----------------------------
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CADFactory.RegisterObject(Line);
|
|
|
|
|
|
|
|
|
|
let l = CADFactory.CreateObject("Line");
|
|
|
|
|
console.log(l);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let db = new Database();
|
|
|
|
|
|
|
|
|
|
console.log(db.hm.Undo());
|
|
|
|
|
|
|
|
|
|
db.hm.Redo();
|