AutoCAD 向图元添加扩展数据(XData)

‘绑定 AutoCAD
Dim I As Integer, J As Integer
Dim oAutoCAD As Object ‘AutoCAD Application 本身
Call BindAutoCAD(True)
Dim oDraw As Object ‘AutoCAD 多文档之一

‘打开并绑定文件 D:/Test.dwg
Set oDraw = oAutoCAD.Application.Documents.Open(“D:/Test.dwg”)

‘绑定 Microsoft Excel
Dim oExcel As Object ‘Microsoft Excel Application 本身
Call BindExcel(True)
Dim oBook As Object ‘Microsoft Excel 多工作薄之一
Dim oSheet As Object ‘工作薄的工作表之一

‘打开并绑定文件 D:/Test.xls
Set oBook = oExcel.Application.WorkBooks.Open(“D:/Test.xls”)

‘绑定已有工作表 Sheet1
Set oSheet = oBook.Sheets(“Sheet1”)

‘屏幕选择图元
Dim PointPicked As Variant
Dim oEntity As Object
oDraw.Activate
oDraw.Utility.GetEntity oEntity, PointPicked

‘指向 TargetRow 行第一列
Dim TargetRow As Integer
TargetRow = MaxRow(oSheet) ‘有文字的最大行
oSheet.Cells(TargetRow, 1).Select

‘添加扩展数据
For J = MinColumn(oSheet) To MaxColumn(oSheet)
If IsInteger(oSheet.Cells(1, J)) And _
IsInRange(oSheet.Cells(1, J), 1000, 1071, True) And _
Not oSheet.Cells(TargetRow, J) = “” Then ‘允许等于上下界
Dim iCount As Integer
iCount = iCount + 1
ReDim Preserve iTypeArray(0 To iCount – 1)
ReDim Preserve sValueArray(0 To iCount – 1)
iTypeArray(iCount – 1) = oSheet.Cells(1, J)
sValueArray(iCount – 1) = oSheet.Cells(TargetRow, J) ‘新数据行位于最后
End If
Next J
‘Debug.Print IsEmptyArray(iTypeArray)
‘Debug.Print IsEmptyArray(sValueArray)
If AttachXData(oEntity, iTypeArray(), sValueArray()) Then
Debug.Print “Attached.”
Else
Debug.Print “Not attached.”
End If

Public Function AttachXData(ByVal oEntity As Object, ByRef iType() As Integer, ByRef sValue() As String) As Boolean
Dim I As Integer, J As Integer
AttachXData = False
If Not IsObject(oEntity) Then Exit Function
If Not IsArray(iType) Then Exit Function
If Not IsArray(sValue) Then Exit Function
If Not LBound(iType) = LBound(sValue) Then Exit Function
If Not UBound(iType) = UBound(sValue) Then Exit Function
If Not IsInArray(“1001”, iType) Then Exit Function
If Not iType(LBound(iType)) = “1001” Then Exit Function
For J = LBound(iType) To UBound(iType)
If Not IsInteger(iType(J)) Then Exit Function
If Trim(sValue(J)) = “” Then Exit Function
Select Case iType(J)
Case 1001 ‘排第一
If CountStr(False, sValue(J), “|”) > 0 Then Exit Function ‘Only one appname is allowed when attaching.
Case 1000, 1002, 1003, 1004, 1005 ‘1002/1004 数据特殊
‘Nothing
Case 1010, 1011, 1012, 1013 ‘三维
For I = 1 To CountStr(False, sValue(J), “|”) + 1
If Not CountStr(False, ExtractDivide(“|”, sValue(J), I), “,”) = 2 Then Exit Function ‘The data format is as nnn,nnn,nnn.
If Not IsNumeric(ExtractDivide(“,”, ExtractDivide(“|”, sValue(J), I), 1)) Then Exit Function
If Not IsNumeric(ExtractDivide(“,”, ExtractDivide(“|”, sValue(J), I), 2)) Then Exit Function
If Not IsNumeric(ExtractDivide(“,”, ExtractDivide(“|”, sValue(J), I), 3)) Then Exit Function
Next I
Case 1040, 1041, 1042
For I = 1 To CountStr(False, sValue(J), “|”) + 1
If Not IsNumeric(ExtractDivide(“|”, sValue(J), I)) Then Exit Function
Next I
Case 1070, 1071
For I = 1 To CountStr(False, sValue(J), “|”) + 1
If Not IsInteger(ExtractDivide(“|”, sValue(J), I)) Then Exit Function
Next I
Case Else
‘Nothing
End Select
Next J
‘DXF 组码值 扩展数据内容
‘1000 字符串, 扩展数据中的字符串可长达 255 字节(第 256 字节是为空字符保留的)
‘1001 应用程序名, 应用名称可长达 31 字节(第 32 字节是为空字符保留的),而且必须符合符号表的名称规则(如图层名称)。应用名称可包含字母、数字、专用字符 $(美元符号)、-(连字符)以及 _(下划线),但不能包含空格
‘1002 控制字符串, 扩展数据的控制字符串可以是”{“或”}”。这些大括号使应用可以通过将扩展数据细分为表来组织它们。左括号开始一个表,右括号结束最新的表。表是可以嵌套的
‘1003 图层名, 与扩展数据相关联的图层名称, 须是已有层
‘1004 二进制数据, 二进制数据被组织为可变长度的数据块,它可在有 ads_binary 结构的 ObjectARX 中处理。数据块的最大长度为 127 字节
‘1005 数据库对象句柄, 图形数据库中的图元句柄
‘1010 三维点(X, Y, Z), 一个点中的三个实数值
‘1011 三维空间位置, 与简单的三维点不同,其 WCS 坐标随扩展数据所属的父图元移动、比例缩放、旋转和镜像。当对父图元使用 STRETCH 命令并且此点位于选择窗口中时,该 WCS 的位置也会被拉伸
‘1012 三维空间距离, 一个随着父图元比例缩放、旋转或镜像的三维点,但不可拉伸或移动
‘1013 三维空间方向, 一个随着父图元旋转或镜像的三维点,但不可比例缩放、拉伸或移动。其 WCS 方向是一个单位长度的规格化向量
‘1040 浮点数, 一个实数值
‘1041 距离值, 一个随着父图元比例缩放的实数值
‘1042 比例系数, 一个随着父图元比例缩放的实数值
‘1070 整数, 一个 16 位整数(带符号或不带符号)
‘1071 长整数, 一个 32 位带符号的(长)整数。如果 1071 组码中出现短整数或实数值,则被转换为一个长整数;如果组码值非法(例如一个字符串),则将被转换为长整数形式的零 (0L)
Dim iCount As Integer
For J = LBound(iType) To UBound(iType)
iCount = iCount + 1
iCount = iCount + CountStr(False, sValue(J), “|”)
Next J
If iCount = 0 Then Exit Function
‘ Initialize all the xdata values. Note that first data in the list should be application name and first datatype code should be 1001
ReDim DataType(0 To iCount – 1) As Integer
ReDim DataValue(0 To iCount – 1) As Variant
iCount = 0 ‘重置
Dim ThreeDim(0 To 2) As Double
For J = LBound(iType) To UBound(iType)
For I = 1 To CountStr(False, sValue(J), “|”) + 1
iCount = iCount + 1
DataType(iCount – 1) = iType(J)
‘Debug.Print DataType(iCount – 1)
If iType(J) = 1010 Or iType(J) = 1011 Or iType(J) = 1012 Or iType(J) = 1013 Then ‘三维
ThreeDim(0) = ExtractDivide(“,”, ExtractDivide(“|”, sValue(J), I), 1)
ThreeDim(1) = ExtractDivide(“,”, ExtractDivide(“|”, sValue(J), I), 2)
ThreeDim(2) = ExtractDivide(“,”, ExtractDivide(“|”, sValue(J), I), 3)
‘Debug.Print ThreeDim(0)
‘Debug.Print ThreeDim(1)
‘Debug.Print ThreeDim(2)
DataValue(iCount – 1) = ThreeDim
Else
DataValue(iCount – 1) = ExtractDivide(“|”, sValue(J), I)
‘Debug.Print DataValue(iCount – 1)
End If
Next I
Next J
‘ Attach the xdata to the entity
oEntity.SetXData DataType, DataValue
AttachXData = True
End Function

鲁迅与顾颉刚

鲁迅为什么这么刻毒地挖苦顾颉刚的“生理缺陷”?照说,鲁迅不是这样的人。因为和鲁迅发生争执的人多了,用聂绀弩的话来说就是“有文皆从人着想,无时不与战为缘”。可无论争论的人有多多,也不管争论得如何激烈,我们都没有看到过鲁迅公开或私下里拿对方生理缺陷做文章。

学过现代文学史或学术史的人都知道:历史上,鲁迅与顾颉刚是一对冤家,彼此之间一提到对方,就忍不住笔露锋芒,骂他个鲜血淋漓。尤其是鲁迅,甚至不惜在历史小说《理水》中,塑造一个很可笑的“鸟头先生”来影射讥讽顾颉刚。比如书中有这样一段:“‘这这些些都是废话’又一个学者吃吃地说,立刻把鼻尖涨得通红。‘你们受了谣言的骗的,其实并没有所谓禹,禹是一条虫,虫虫会治水吗?’”为什么这样影射呢?原因是顾颉刚根据文字学将“禹”解为“蜥蜴”,从而得出“夏禹是一条虫”的结论。而鲁迅以其人之道还治其人之身地根据文字学将“顾(顾)”字分解为“雇”(本义为“鸟”)与“页”(本义为“头”)。所以,不少人说鲁迅这是在利用小说进行“人身攻击”。因为这让人想起《水浒》中的骂人话,“鸟人”!而在鲁迅的私人通信里,则直接将顾颉刚称为“鼻公”、“鼻”、或“红鼻”。比如在1927年5月15日致章廷兼的信中,鲁迅就写到:“傅斯年我初见,先前竟想不到是这样的人,当红鼻到此时,我便走了;而傅大写其信给我,说他已有补救发,即使鼻赴京买书,不在校……”再比如同年8月17日鲁迅致章廷兼的信中又有:“遥想一月以前,一个獐头鼠目而赤鼻之‘学者’,奔波于‘西子湖’而发挥咱们之‘不好’,一面又想起起诉之‘无聊之极思’来。湖光山色,辜负已尽,念及辄为失笑。禹是虫,故无其人;而据我最近之研究:迅盖禽也,亦无其人,鼻当可以自慰欤?……近偶见《古史辨》,惊悉上面乃有自序一百多版。查汉朝钦犯司马迁,因割掉卵子而发牢骚,附之于偌大之《史记》之后,文尚甚短,今该学者不过鼻子红而已矣,而乃浩浩洋洋至此,殆真所谓文豪也哉,禹而尚在,也只能忍气吞声,自认为并无其人而已。”为什么这样称呼呢?原因就是顾颉刚长了一个红红的酒糟鼻。这当然也是很不厚道的人身攻击。所以有人认为这是鲁迅拿人家生理缺陷做文章的“失德之举”。

现在的问题是:鲁迅为什么这么刻毒地挖苦顾颉刚的“生理缺陷”?照说,鲁迅不是这样的人。因为和鲁迅发生争执的人多了,用聂绀弩的话来说就是“有文皆从人着想,无时不与战为缘”。可无论争论的人有多多,也不管争论得如何激烈,我们都没有看到过鲁迅公开或私下里拿对方生理缺陷做文章。不特此也:1922年,当俄国盲诗人爱罗先珂来北京时,因为写文章批评了北京学生上演的戏剧,结果惹怒了这群天之骄子,立即就有北大学生魏建功写出一篇《不敢盲从》以为回敬。文中,作者故意在“看”、“观”、“盲从”等字上大做文章,大搞人身攻击,而这引起了鲁迅的强烈反感。他马上写出了《看魏建功君〈不敢盲从〉以后的几句声明》。在文中,鲁迅怒斥这种利用别人生理缺陷对其进行攻击的人,是“生长在旧的道德和新的不道德里,借了新艺术的名而发挥其本来的旧的不道德的少年”。在1919年3月26日,为《孔乙己》做“附记”时,鲁迅也曾明确反对用小说进行人身攻击,使小说成为一种泼秽水的器具。既然如此,那鲁迅为什么对顾颉刚有这样的“失德之举”?顾颉刚到底什么地方开罪了鲁迅,使他这样不顾自己原则地,对其生理缺陷一再实施旁人看来甚是过分的攻击?而且,这种怨恨一直持续到鲁迅晚年。比如在1934年7月6日鲁迅致郑振铎的信中,提到顾颉刚时,仍然这么刻薄地写到“三根(指顾颉刚,因为在中国古代相面语中,‘三根’即指鼻梁———笔者)是必显神通的,但此公遍身谋略,凡与接触者,定必麻烦,倘与周旋,本亦不足惧,然别人那有如许闲工夫。嘴亦本来不吃,其呐呐者,即因岁谈话时,亦在运用阴谋之故。在厦大时,即逢迎校长以驱除异己,异己既尽,而此公亦为校长所鄙,遂至广州,我连忙逃走,不知其又何以不安于粤也。现在所发之狗性,盖于在厦大时相同。最好不要与相涉,否则钩心斗角之事,层出不穷,真使人不胜其扰。其实,他是有破坏而无建设的,只要看他的《古史辨》已将古史‘辨’得没有,自己也不再有路可走,只好又用老手段了。”

数值函数系列之一

‘将任意整数分解为等比数列之中的数值
Dim vRet As Variant
vRet = GeometricSeriesEx(2147483647)
For I = LBound(vRet) To UBound(vRet)
Debug.Print vRet(I)
Next I

Public Function GeometricSeriesEx(ByVal lData As Long) As Variant ‘分解整数为 1 2 4 8 16 32 64 … …,且每个数只出现一次
Dim I As Integer, J As Integer
If Not IsNumericEx(lData) Then Exit Function
Dim sBin As String
sBin = DEC_to_BIN(lData)
Dim iCount As Integer
Dim TempArray() As Long
For I = 1 To Len(sBin)
If Mid(sBin, I, 1) = “1” Then
ReDim Preserve TempArray(0 To iCount)
TempArray(iCount) = 2 ^ (Len(sBin) – I)
iCount = iCount + 1
End If
Next I
GeometricSeriesEx = TempArray
End Function

Public Function DEC_to_BIN(Dec As Long) As String ‘十进制转化为二进制
DEC_to_BIN = “”
Do While Dec > 0
DEC_to_BIN = Dec Mod 2 & DEC_to_BIN
Dec = Dec \ 2
Loop
End Function

Public Function IsNumericEx(ByVal sData As String) As Boolean ‘电子表格空格被 IsNumeric 视为数值 0,IsNumericEx 则视为字符 “”
IsNumericEx = False
sData = Trim(sData)
If sData = “” Then Exit Function
IsNumericEx = IsNumeric(sData)
End Function

Public Function IsInteger(ByVal txtString As String) As Boolean ‘是数值,且是整数
IsInteger = True
If Not IsNumeric(txtString) Then
IsInteger = False
Exit Function
End If
If Not CDbl(txtString) / 1 = CDbl(txtString) \ 1 Then
IsInteger = False
End If
End Function

Public Function IsInRange(ByVal sData As String, ByVal LowerLimit As Double, ByVal UpperLimit As Double, ByVal bAllowThreshold As Boolean) As Boolean ‘判断某数据是不是数值,且是否在界内
IsInRange = False
If Not IsNumeric(sData) Then Exit Function ‘字符等视为越界,防止以下比较时发生错误
If bAllowThreshold Then ‘压界算在界内
If CDbl(sData) > UpperLimit Then Exit Function ‘越上界
If CDbl(sData) < LowerLimit Then Exit Function ‘越下界 Else ‘压界不算在界内 If CDbl(sData) >= UpperLimit Then Exit Function ‘越上界
If CDbl(sData) <= LowerLimit Then Exit Function ‘越下界
End If
IsInRange = True
End Function

二十八星宿对应分野

角、亢、(氏)〔氐〕,鄭,兗州:東郡入角一度;東平、任城、(由)〔山〕陽入角六度;泰山入角十二度;濟北、陳留入亢五度;濟陰入氐一度;東平入氐七度。

  房、心,宋,豫州:潁川入房一度;汝南入房二度;沛郡入房四度;梁國入房五度;淮陽入心一度;魯國入心三度;楚國入心四度。

  箕、尾,燕,幽州:營州(人)〔入〕箕中十度;上谷入尾一度;漁陽入尾三度;右北平入尾七度;西河、上郡、北地、遼西東入尾十度;涿郡入尾十六度;渤海入箕一度;樂浪入箕三度;玄菟入箕六度;廣陽入箕九度。

  斗、牽牛、須女,吳、越,揚州:九江入斗一度;廬江入斗六度;豫章入斗十度;丹陽入斗十六度;會稽入牛一度;臨淮入牛四度;廣陵入牛八度;泗水入女一度;六安入牛六度。

  虛、危,齊,青州:齊國入虛六度;北海入虛九度;濟南入危一度;樂安入危四度;東萊入危九度;平原入危十一度;淄州入危十四度。

  營室、東壁,衛,并州:安定入營室一度;天水入營室八度;隴西入營室四度;酒泉入營室十一度;張掖入營室十二度;武都入東壁一度;金城入東壁四度;武威入東壁六度;燉(威)〔煌〕入東壁八度。

  奎、婁、胃,魯,徐州:東海入奎一度;瑯邪入奎一度;琅密入婁一度;(陽城)〔城陽〕入婁九度;膠東入胃(七)〔一〕度。

  昴、畢,趙,冀州:魏郡入昴一度;鉅鹿入昴三度;常山入昴五度;廣平入昴七度;中山入昴一度;清河入昴九度;信都入(十)〔畢〕三度;趙郡入畢八度;安平入畢四度;河間入畢十度;真定入畢十三度。

  觜、參,魏,益州:廣漢入觜一度;越巂入觜三度;蜀郡入觜一度;犍為入參三度;牁入參五度;巴郡入參五度;漢中入參九度;益州入參七度。

  東井、輿鬼,秦,雍州:雲中入東井一度;定襄入東井八度;雁門入東井十六度;代郡入東井二十八度;太原入東井二十九度;上黨入輿鬼二度。

  柳、七星、張,周,三輔:弘農入柳一度;河南入七星三度;河東入張三度;河內入張九度。

  翼、軫,楚,荊州:南陽入翼六度;南郡入翼十度;江夏入翼十二度,零陵入軫十一度;桂陽入軫六度;武陵入軫十度,長沙入軫十六度。

Excel 函数系列之一

Public Function MinRow(ByVal vSheet As Variant) As Integer ‘有内容的最小行号
MinRow = -1
On Error Resume Next
If vSheet Is Nothing Then Exit Function
vSheet.Activate
MinRow = vSheet.UsedRange.Rows(vSheet.UsedRange.Rows.Count).Row – vSheet.UsedRange.Rows.Count + 1
On Error GoTo 0
End Function

Public Function MaxRow(ByVal vSheet As Variant) As Integer ‘有内容的最大行号
MaxRow = -1
On Error Resume Next
If vSheet Is Nothing Then Exit Function
vSheet.Activate
MaxRow = vSheet.UsedRange.Rows(vSheet.UsedRange.Rows.Count).Row
On Error GoTo 0
End Function

Public Function MinColumn(ByVal vSheet As Variant) As Integer ‘有内容的最小列号
MinColumn = -1
On Error Resume Next
If vSheet Is Nothing Then Exit Function
vSheet.Activate
MinColumn = vSheet.UsedRange.Columns(vSheet.UsedRange.Columns.Count).Column – vSheet.UsedRange.Columns.Count + 1
‘vSheet.Cells.Find(“*”, , , , 2, 1).Column 同
On Error GoTo 0
End Function

Public Function MaxColumn(ByVal vSheet As Variant) As Integer ‘有内容的最大列号
MaxColumn = -1
On Error Resume Next
If vSheet Is Nothing Then Exit Function
vSheet.Activate
MaxColumn = vSheet.UsedRange.Columns(vSheet.UsedRange.Columns.Count).Column
‘vSheet.Cells.Find(“*”, , , , 2, 2).Column 同
If Err.Number <> 0 Then Call SaveOccurredErr(1)
If Not ExposeError Then On Error GoTo 0
End Function

Public Function SheetExist(ByVal vBook As Variant, ByVal sSheet As String) As Boolean ‘当前 WorkBook 之 Sheet 存在
On Error Resume Next
Dim I As Integer, J As Integer
SheetExist = False
If vBook Is Nothing Then Exit Function
‘If vSheet Is Nothing Then Exit Function
For I = 1 To vBook.Sheets.Count
If vBook.Sheets(I).Name = sSheet Then SheetExist = True
Next I
If Err.Number <> 0 Then SheetExist = False
On Error GoTo 0
End Function

Public Function GetColumnNumber(ByVal vSheet As Variant, ByVal sData As String, ByVal iRow As Integer) As Integer ‘确定数据在某行的哪一列,如果不存在则指向新列
Dim I As Integer, J As Integer
GetColumnNumber = 0
If vSheet Is Nothing Then Exit Function
vSheet.Activate
For J = MinColumn(vSheet) To MaxColumn(vSheet)
If sData = vSheet.Cells(iRow, J) Then GetColumnNumber = J
Next J
If GetColumnNumber = 0 Then GetColumnNumber = MaxColumn(vSheet) + 1
End Function