[Solved] Change a line to a connector programmatically

Creating a macro - Writing a Script - Using the API (OpenOffice Basic, Python, BeanShell, JavaScript)
Post Reply
bowcoastie
Posts: 7
Joined: Wed Jan 22, 2014 3:16 am

[Solved] Change a line to a connector programmatically

Post by bowcoastie »

Hello All,
I have many drawings that I need to change lines into connectors. Is it possible to convert a Line to a Connector either programmatically or through the user interface?

I am running OpenOffice 4.0 on a Windows 7 machine.

Thank you for your help.
Last edited by Hagar Delest on Sat Feb 01, 2014 12:58 am, edited 1 time in total.
Reason: tagged [Solved].
OpenOffice 4.1 on Windows 7
User avatar
Charlie Young
Volunteer
Posts: 1559
Joined: Fri May 14, 2010 1:07 am

Re: Change a line to a connector programmatically

Post by Charlie Young »

I think you need to post an example document showing what kind of arrangement of shapes and lines you have.

I don't think it is possible to change a line to a connector, but it is possible to create a new connector via a program. But of course then one needs to know what to connect it to. I'm thinking if you have lines drawn, it might be possible to determine the nearest gluepoint of a shape, but I need a clearer picture of what I'm up against.
Apache OpenOffice 4.1.1
Windows XP
bowcoastie
Posts: 7
Joined: Wed Jan 22, 2014 3:16 am

Re: Change a line to a connector programmatically

Post by bowcoastie »

Hello Charlie,
I was thinking about it today and came to a similar conclusion. I was thinking that I could select the line to convert a connector and then run a macro that would use its attributes to make a new connector. Using the nearest available glue point would make sense.

I've attached an example. I want to change some of the lines that show the process flow to connectors to make it easier to rearrange the pieces without having to redraw the lines. This is a very simple example and many of my drawings are process flow diagrams used in chemical plants that can have thousands of elements.

I am very familiar with VBA but am just starting on OpenOffice. I am just so tired of having legacy problems with MS using proprietary file encodings :crazy: that I have to convert to something far more stable. The plants can go for 50 years and this is the 4th time we've changed file formats in 10 years. I am not doing it again lol/



I sincerely appreciate the help.


Regards,


Bruce
Attachments
Pressure Control diagram.odg
Example of what I need to do regarding changing lines to connectors.
(11.08 KiB) Downloaded 254 times
OpenOffice 4.1 on Windows 7
User avatar
Charlie Young
Volunteer
Posts: 1559
Joined: Fri May 14, 2010 1:07 am

Re: Change a line to a connector programmatically

Post by Charlie Young »

it should be helpful that you're a programmer certainly. You'll need to consult the API documentation, particularly the drawing API.

In the first experiments I conducted before I posted here, I restricted myself to plain LineShapes connecting to RectangleShapes. Your attached document seems to mostly involve PolyLineShapes and GroupShapes, so that is what I'm focusing on for now. I suspect it will ultimately prove to be an oversimplification, but we've got to start somewhere.

The pieces we need for starters:

This gets the coordinates of all the GluePoints of a shape. If the GluePoint IsRelative then the coordinates are relative to the page, and no adjustment is needed. If IsRelative is False though, and it seems it always is, then the GluePoint positions are with respect to the shape's center, and we need to adjust to get the page coordinates accordingly.

Code: Select all

Function GlueCoords(oShape As Object)
	Dim ShapeCenter As New com.sun.star.awt.Point
	Dim oGlue
	Dim g As Long
	Dim oPos As New com.sun.star.awt.Point
	Dim oSize As New com.sun.star.awt.Size
	Dim GluePointCoords(0) As New com.sun.star.awt.Point
	Dim oPoint As New com.sun.star.drawing.GluePoint2
	
	oPos = oShape.getPosition()
	oSize = oShape.getSize()
	
	ShapeCenter.X = oPos.X + oSize.Width\2
	ShapeCenter.Y = oPos.Y + oSize.Height\2
	
	oGlue = oShape.getGluePoints()
	For g = 0 to oGlue.getCount() - 1
		oPoint = oGlue.getByIndex(g)
		Redim Preserve GluePointCoords(g) As com.sun.star.awt.Point
		If oPoint.IsRelative Then
			GluePointCoords(g).X = oPoint.Position.X
			GluePointCoords(g).Y = oPoint.Position.Y
		Else
			GluePointCoords(g).X = ShapeCenter.X + oPoint.Position.X
			GluePointCoords(g).Y = ShapeCenter.Y + oPoint.Position.Y
		End If
	Next g
	GlueCoords = GluePointCoords
End Function
Then we need a simple function to get the distance between two points

Code: Select all


Function Distance(a As com.sun.star.awt.Point, b As com.sun.star.awt.Point) As Double
	Distance = Sqr((a.X - b.X)^2 + (a.Y - b.Y)^2)
End Function
With that, select a PolyLine in the UI, and run this

Code: Select all


Sub PolyLine2Connector
	Dim oDoc As Object
	Dim oConnector As Object
	Dim oShapes(0) As Object, oShape As Object
	Dim oDP As Object
	Dim oSelection As Object, oLine As Object
	Dim i As Long, j As Long, k As Long, n As Long
	Dim oCoords, PolyPoints
	Dim EndPoints(1) As New com.sun.star.awt.Point
	Dim MinDistance(1) As Double, MinShapeIndex(1) As Long, MinGlueIndex(1) As Long
	Dim Dist As Double
	
	oDoc = ThisComponent
	oSelection = oDoc.getCurrentController().getSelection()
	oLine = oSelection.getByIndex(0)
	'Make sure selection is a PolyLine
	
	If oLine.getShapeType() <> "com.sun.star.drawing.PolyLineShape" Then
		MsgBox("No PolyLine Selected")
		Exit Sub
	End If
	
	'Get EndPoints of the polyline
	PolyPoints = oLine.getPropertyValue("Polygon")
	
	EndPoints(0).X = PolyPoints(0).X
	EndPoints(0).Y = PolyPoints(0).Y
	EndPoints(1).X = PolyPoints(UBound(PolyPoints)).X
	EndPoints(1).Y = PolyPoints(UBound(PolyPoints)).Y
	
	oDP = oDoc.getDrawPages().getByIndex(0)
	'Make a connector
	oConnector = oDoc.createInstance("com.sun.star.drawing.ConnectorShape")
	oConnector.EdgeKind = com.sun.star.drawing.ConnectorType.STANDARD
	i = 0
	n = 0
	'Get all the GroupShapes
	Do While i < oDP.getCount()
		oShape = oDP.getByIndex(i)
		If oShape.supportsService("com.sun.star.drawing.GroupShape") Then
			Redim Preserve oShapes(n) As Object
			oShapes(n) = oShape
			n = n + 1
		EndIf	
		i = i + 1
	Loop
	
	'Find closest GluePoint to each EndPoint 
	For i = 0 To 1
		oCoords = GlueCoords(oShapes(0))
		MinShapeIndex(i) = 0
		MinGlueIndex(i) = 0
		MinDistance(i) = Distance(EndPoints(i),oCoords(0))
		For j = 0 To n - 1
			oCoords = GlueCoords(oShapes(j))
			For k = 0 To UBound(oCoords)
				Dist = Distance(EndPoints(i),oCoords(k))
				If Dist < MinDistance(i) Then
					MinDistance(i) = Dist
					MinShapeIndex(i) = j
					MinGlueIndex(i) = k
				End If
			Next k
		Next j
	Next i
	
	'Connect the connector to the closest Gluepoints
	oDP.add(oConnector)
	oConnector.setPropertyValue("StartShape",oShapes(MinShapeIndex(0)))
	oConnector.setPropertyValue("StartGluePointIndex",MinGlueIndex(0))
	oConnector.setPropertyValue("EndShape",oShapes(MinShapeIndex(1)))
	oConnector.setPropertyValue("EndGluePointIndex",MinGlueIndex(1))
	'Remove original line
	oDP.remove(oLine)
End Sub

Like I said, this probably is ultimately a special case, but hopefully it's a start.
Attachments
PressureControldiagram_X1.odg
Version 1
(14.23 KiB) Downloaded 264 times
Apache OpenOffice 4.1.1
Windows XP
bowcoastie
Posts: 7
Joined: Wed Jan 22, 2014 3:16 am

Re: Change a line to a connector programmatically

Post by bowcoastie »

Dear Charlie,
Thank you so much! That goes a long way toward my goal!!!!! My next biggest problem is getting my head around the API. I've been spoiled by the VBA .NET IDE that serves the properties up on a silver platter.

Is there something that will help me explore the API or is it just available as a text document?


Thank you again.
OpenOffice 4.1 on Windows 7
User avatar
Charlie Young
Volunteer
Posts: 1559
Joined: Fri May 14, 2010 1:07 am

Re: Change a line to a connector programmatically

Post by Charlie Young »

bowcoastie wrote:Dear Charlie,
Is there something that will help me explore the API or is it just available as a text document?
You will need hanya's MRI and/or Bernard Marcelly's XRay, and yes, you will still need to cross-reference the online API documentation, though note also that there are many examples in the Wiki, in the forums (both these and the old ones), and Andrew Pitonyak's stuff. Andrew, hanya, and Bernard all peek in here from time to time.
Apache OpenOffice 4.1.1
Windows XP
bowcoastie
Posts: 7
Joined: Wed Jan 22, 2014 3:16 am

Re: Change a line to a connector programmatically

Post by bowcoastie »

Hello Charlie,
You are truly a godsend. :super:

Thank you for all your support. I'll work getting this up an running over the next couple of days.

Warmest Regards,


Bruce
OpenOffice 4.1 on Windows 7
User avatar
Charlie Young
Volunteer
Posts: 1559
Joined: Fri May 14, 2010 1:07 am

Re: Change a line to a connector programmatically

Post by Charlie Young »

You're welcome.

I guess I'll correct a couple of, I think, inconsequential items in my earlier code.

in PolyLine2Connector, there is no need for MinDistance to be an array, just use MinDistance as a scalar wherever MinDistance() appears.

That probably doesn't matter at all, but in GlueCoords, I am needlessly using ReDim for each g, when we know the needed array size in advance

Code: Select all

Function GlueCoords(oShape As Object)
	Dim ShapeCenter As New com.sun.star.awt.Point
	Dim oGlue
	Dim g As Long
	Dim oPos As New com.sun.star.awt.Point
	Dim oSize As New com.sun.star.awt.Size
	Dim oPoint As New com.sun.star.drawing.GluePoint2
	
	oPos = oShape.getPosition()
	oSize = oShape.getSize()
	
	ShapeCenter.X = oPos.X + oSize.Width\2
	ShapeCenter.Y = oPos.Y + oSize.Height\2
	
	oGlue = oShape.getGluePoints()
	Dim GluePointCoords(oGlue.getCount() - 1) As New com.sun.star.awt.Point
	For g = 0 to oGlue.getCount() - 1
		oPoint = oGlue.getByIndex(g)
		If oPoint.IsRelative Then
			GluePointCoords(g).X = oPoint.Position.X
			GluePointCoords(g).Y = oPoint.Position.Y
		Else
			GluePointCoords(g).X = ShapeCenter.X + oPoint.Position.X
			GluePointCoords(g).Y = ShapeCenter.Y + oPoint.Position.Y
		End If
	Next g
	GlueCoords = GluePointCoords
End Function


That probalby doesn't matter much either, but i can see where it might improve speed and allocation efficiency if there are many shapes.

Another thing I have been looking at is the business with PolyLines. In Your example that's mostly what we're dealing with, but the line going to the left from the Control Valve is a simple LineShape, and since its left terminus (with the arrow), is on a GroupShape, it is easy to include such a LineShape in the code

Code: Select all

Sub Line2Connector
	Dim oDoc As Object
	Dim oConnector As Object
	Dim oShapes(0) As Object, oShape As Object
	Dim oDP As Object
	Dim oSelection As Object, oLine As Object
	Dim i As Long, j As Long, k As Long, n As Long
	Dim oCoords, PolyPoints
	Dim EndPoints(1) As New com.sun.star.awt.Point
	Dim MinDistance As Double, MinShapeIndex(1) As Long, MinGlueIndex(1) As Long
	Dim Dist As Double
	
	oDoc = ThisComponent
	oSelection = oDoc.getCurrentController().getSelection()
	oLine = oSelection.getByIndex(0)
	'Make sure selection is a PolyLine or a line.
	
	If oLine.getShapeType() <> "com.sun.star.drawing.PolyLineShape" And oLine.getShapeType() <> "com.sun.star.drawing.LineShape" Then
		MsgBox("No Line Selected")
		Exit Sub
	End If
	
	'Get EndPoints of the polyline
	PolyPoints = oLine.getPropertyValue("Polygon")
	
	EndPoints(0).X = PolyPoints(0).X
	EndPoints(0).Y = PolyPoints(0).Y
	EndPoints(1).X = PolyPoints(UBound(PolyPoints)).X
	EndPoints(1).Y = PolyPoints(UBound(PolyPoints)).Y
	
	oDP = oDoc.getDrawPages().getByIndex(0)
	'Make a connector
	oConnector = oDoc.createInstance("com.sun.star.drawing.ConnectorShape")
	oConnector.EdgeKind = com.sun.star.drawing.ConnectorType.STANDARD
	i = 0
	n = 0
	'Get all the GroupShapes
	Do While i < oDP.getCount()
		oShape = oDP.getByIndex(i)
		If oShape.supportsService("com.sun.star.drawing.GroupShape") Then
			Redim Preserve oShapes(n) As Object
			oShapes(n) = oShape
			n = n + 1
		EndIf	
		i = i + 1
	Loop
	
	'Find closest GluePoint to each EndPoint 
	For i = 0 To 1
		oCoords = GlueCoords(oShapes(0))
		MinShapeIndex(i) = 0
		MinGlueIndex(i) = 0
		MinDistance = Distance(EndPoints(i),oCoords(0))
		For j = 0 To n - 1
			oCoords = GlueCoords(oShapes(j))
			For k = 0 To UBound(oCoords)
				Dist = Distance(EndPoints(i),oCoords(k))
				If Dist < MinDistance Then
					MinDistance = Dist
					MinShapeIndex(i) = j
					MinGlueIndex(i) = k
				End If
			Next k
		Next j
	Next i
	
	'Connect the connector to the closest Gluepoints
	oDP.add(oConnector)
	oConnector.setPropertyValue("StartShape",oShapes(MinShapeIndex(0))) 
	oConnector.setPropertyValue("StartGluePointIndex",MinGlueIndex(0))
	oConnector.setPropertyValue("EndShape",oShapes(MinShapeIndex(1)))
	oConnector.setPropertyValue("EndGluePointIndex",MinGlueIndex(1))
	'Remove original line
	oDP.remove(oLine)
End Sub
As I supposed though, we'll run into difficulties. The line going left from the Pressure Gauge doesn't end at a Gluepoint, it just bumps into a vertical line. So if the macro is run on the line going left, it winds up connected to the wrong thing.

Another glitch was when I ran it on the PolyLine going into the top of the Vacuum Pump, since the PolyLine extended left, it wound up attaching to the left side of the pump instead of the top. I want to mention these things because I imagine you'll run into many such problems.

I also will say, though I'm sure you realize it, that someone very knowledgeable about the engineering better look at these things after the changes.
Apache OpenOffice 4.1.1
Windows XP
bowcoastie
Posts: 7
Joined: Wed Jan 22, 2014 3:16 am

Re: Change a line to a connector programmatically

Post by bowcoastie »

Hello Charlie,
As far as the minor imperfections in getting the glue points right, it's not a real issue as I will have to manually connect them if they are out. I got the code running and added a button to the tool bar; it works really well!!!! Now I just need to add it to the right click menu and I'll be going even faster!!! I can't thank you enough for your help and professionalism.

This is the first time I have ever reached out for help on a programming matter (I was just too far out of my element); I am shocked to say the least! I am really grateful.

Thank you again. If you get to the New Orleans area, let me know and we can do lunch.

Regards,


Bruce
OpenOffice 4.1 on Windows 7
Post Reply