Responding to Manipulation delta - strange effect and overflow - touch

In a test UWP form I have a basic manipulation test, code below. It draws 3 circles on a CanvasControl and sets up translation and scaling manipulation.
When I test this on a touch screen it basically does what I expect, translating and zooming the circles based on the position of 2 fingers on the screen. But if I pinch down beyond a certain point, the image starts to oscillate very quickly between 2 extents and will very quickly cause the code to stop with an overflow.
I put the canvas control in a grid and tried doing the manipulation on the canvas control from the grid control, and it does not suffer from the same problem although the effect of zooming and panning does not seem correct.
So it looks like the effect of my code as is, is an iteration, where a manipulation causing a render transform change could cause another manipulation, and it goes in circles until it settles - or if there is a problem of precision, perhaps due to the distance between the touch points getting too small, the iteration diverges until overflow.
Is this expected? What is the correct way to do this?
Private WithEvents Canv As New CanvasControl
Private WithEvents gr As New Grid
Private Sub Canv_Draw(sender As CanvasControl, args As CanvasDrawEventArgs) Handles Canv.Draw
args.DrawingSession.DrawCircle(50, 50, 25, Windows.UI.Colors.Blue)
args.DrawingSession.DrawCircle(250, 250, 25, Windows.UI.Colors.Blue)
args.DrawingSession.DrawCircle(500, 500, 25, Windows.UI.Colors.Blue)
End Sub
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Content = gr
gr.Children.Add(Canv)
Canv.ManipulationMode = ManipulationModes.Scale Or ManipulationModes.TranslateX Or ManipulationModes.TranslateY
end sub
Private Sub Canv_ManipulationDelta(sender As Object, e As ManipulationDeltaRoutedEventArgs) Handles Canv.ManipulationDelta
Dim t As New TranslateTransform
t.X = e.Cumulative.Translation.X
t.Y = e.Cumulative.Translation.Y
Dim s As New ScaleTransform
s.ScaleX = e.Cumulative.Scale
s.ScaleY = e.Cumulative.Scale
s.CenterX = e.Position.X
s.CenterY = e.Position.Y
Dim g As New TransformGroup
g.Children.Add(s)
g.Children.Add(t)
Canv.RenderTransform = g
End Sub

The common way in UWP is to use CompositeTransform, it supports Scale, Skew, Rotate and Translate.
Please see the BasicInput sample, especially the forth scenario
For the zooming issue, you can avoid it by using the following way:
Public NotInheritable Class MainPage
Inherits Page
Private WithEvents Canv As New CanvasControl
Private WithEvents gr As New Grid
Private Sub Canv_Draw(sender As CanvasControl, args As CanvasDrawEventArgs) Handles Canv.Draw
args.DrawingSession.DrawCircle(50, 50, 25, Windows.UI.Colors.Blue)
args.DrawingSession.DrawCircle(250, 250, 25, Windows.UI.Colors.Blue)
args.DrawingSession.DrawCircle(500, 500, 25, Windows.UI.Colors.Blue)
End Sub
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Content = gr
gr.Children.Add(Canv)
Canv.ManipulationMode = ManipulationModes.Scale Or ManipulationModes.TranslateX Or ManipulationModes.TranslateY
End Sub
Private Sub Canv_ManipulationDelta(sender As Object, e As ManipulationDeltaRoutedEventArgs) Handles Canv.ManipulationDelta
Dim tran = Transform(sender)
tran.ScaleX = tran.ScaleX * e.Delta.Scale
tran.ScaleY = tran.ScaleY * e.Delta.Scale
'System.Diagnostics.Debug.WriteLine("tran.ScaleX =" + tran.ScaleX.ToString() + " tran.ScaleY =" + tran.ScaleY.ToString())
End Sub
Private Function Transform(sender As Object) As CompositeTransform
Dim rect = TryCast(sender, CanvasControl)
rect.RenderTransformOrigin = New Point(0.5, 0.5)
Dim tran As New CompositeTransform
If TryCast(rect.RenderTransform, CompositeTransform) IsNot Nothing Then
tran = DirectCast(rect.RenderTransform, CompositeTransform)
Else
rect.RenderTransform = New CompositeTransform()
End If
Return tran
End Function
' utility method
Private Function Boundary(value As Double, min As Double, max As Double) As Double
If value > max Then
Return max
ElseIf value < min Then
Return min
Else
Return value
End If
End Function
End Class
Screenshot:

Some useful information in that, in the meantime I found a tidy solution, which was to leave the CanvasControl in the grid, and take the manipulation events from the grid and change the rendertransform of the Canvas control, that way there is no recursion. It also means that the natural translation is not correct - but this is easy to fix by multiplying it by the cumulative scale, ie. the full manipulation code becomes:
Private Sub GerberCanvGrid_ManipulationDelta(sender As Object, e As ManipulationDeltaRoutedEventArgs) Handles GerberCanvGrid.ManipulationDelta
Dim sf As New ScaleTransform
sf.ScaleX = e.Cumulative.Scale
sf.ScaleY = e.Cumulative.Scale
sf.CenterX = e.Position.X
sf.CenterY = e.Position.Y
Dim tt As New TranslateTransform
tt.X = e.Cumulative.Translation.X * e.Cumulative.Scale
tt.Y = e.Cumulative.Translation.Y * e.Cumulative.Scale
ManipulationTransform = New TransformGroup
ManipulationTransform.Children.Add(sf)
ManipulationTransform.Children.Add(tt)
GerberCanv.RenderTransform = ManipulationTransform
End Sub

Related

Gmap.Net save image around selected marker

I have an application with GMap.Net showing various markers. I know how to take a screen shot of the current map and markers:
Dim sImageName As String = DateTime.Now.ToString(Format("yyyyMMdd-HHmmss")) & ".png"
Dim ThisMap As New Bitmap(Form2.myMap.Width, Form2.myMap.Height)
Form2.myMap.DrawToBitmap(ThisMap, New Rectangle(0, 0, Form2.myMap.Width, Form2.myMap.Height))
ThisMap.Save(sImagesFolder & sImageName)
What I would like to do is create an image for a selected marker. Instead of the image being the entire map shown on screen, it would center on the marker and show 100 pixels in each direction.
Does anyone know how to do that?
This is what I tried, but it gives me a blank image-- nothing shows up. I feel like this should be working...
Private Sub MyMap_OnMarkerClick(item As GMapMarker, e As Windows.Forms.MouseEventArgs) Handles myMap.OnMarkerClick
SelMarkerX = e.X
SelMarkerY = e.Y
Dim sImageName As String = DateTime.Now.ToString(Format("yyyyMMdd-HHmmss")) & ".png"
Dim ThisMap As New Bitmap(140,100)
myMap.DrawToBitmap(ThisMap, New Rectangle(SelMarkerX - 70, SelMarkerY - 50, 140, 100))
ThisMap.Save(sImagesFolder & sImageName)
End Sub
I just don't get it. If I write:
myMap.DrawToBitmap(ThisMap, New Rectangle(0, 0, 140, 100)
then I get what you might expect. I get the upper left corner of the existing map from 0 to 140 horizontally and 0 to 100 vertically. If I change it to this:
myMap.DrawToBitmap(ThisMap, New Rectangle(10, 0, 140, 100)
then I get 0 to 130 horizontally and not 10 to 140.
Well, I couldn't figure out how to do it with Gmap, so I wondered if I could crop it outside of Gmap and apparently that is common. Here is the code I used.
Dim ThisMap As New Bitmap(Form2.myMap.Width, Form2.myMap.Height)
Form2.myMap.DrawToBitmap(ThisMap, New Rectangle(0, 0, Form2.myMap.Width, Form2.myMap.Height))
ThisMap.Save(sImagesFolder & sImageName)
Dim LocX = SelMarkerX - 160 'x cord. of where crop starts
Dim LocY = SelMarkerY - 120 'y cord. of where crop starts
Dim CropW = 320 'Crop width
Dim CropH = 240 'Crop height
Dim CropRect As New Rectangle(LocX, LocY, CropW, CropH)
Dim OriginalImage = ThisMap
Dim CropImage = New Bitmap(CropRect.Width, CropRect.Height)
Using grp = Graphics.FromImage(CropImage)
grp.DrawImage(OriginalImage, New Rectangle(0, 0, CropRect.Width, CropRect.Height), CropRect, GraphicsUnit.Pixel)
CropImage.Save(sImagesFolder & sImageName)
End Using

simplest Unostructure that supports he getByName

In LibreOffice Basic sub I use a bunch of uno properties in an array. Which is the simplest Unostructure or UnoService that I must "embed" them, in order to use the getByName "function"?
Example:
dim props(1) as new com.sun.star.beans.PropertyValue
props(0).Name = "blahblah1"
props(0).Value = "blahblah1Value"
props(1).Name = "blahblah2"
props(1).Name = 3000
I want to be able to use something like:
b = props.getByName("blahblah2").Value
or something like (assuming I "assigned" them in a structure-like-object called "somestruct") :
b = somestruct.getprops.getByName("blahblah2").Value
As I understand that this can be done by creating a "UnoService" which supports the getByName and then, somehow, assigning these props to this service
Which is the "lightest" such service?
(I mean the service that uses less resources)
Thanks in advance.
Really supporting the interface XNameAccess is not as easy. The services which implement this interface are supposed using this interface for existing named properties, not for own created ones.
But you can use the service EnumerableMap to achieve what you probably want.
Example:
sub testEnumerableMap
serviceEnumerableMap = com.sun.star.container.EnumerableMap
oEnumerableMap = serviceEnumerableMap.create("string", "any")
oEnumerableMap.put("blahblah1", "blahblah1Value")
oEnumerableMap.put("blahblah2", 3000)
oEnumerableMap.put("blahblah3", 1234.67)
msgbox oEnumerableMap.get("blahblah1")
msgbox oEnumerableMap.get("blahblah2")
msgbox oEnumerableMap.get("blahblah3")
'msgbox oEnumerableMap.get("blahblah4") 'will throw error
msgbox oEnumerableMap.containsKey("blahblah2")
msgbox oEnumerableMap.containsValue(3000)
if oEnumerableMap.containsKey("blahblah4") then
msgbox oEnumerableMap.get("blahblah4")
end if
end sub
But starbasic with option Compatible is also able supporting Class programming like VBA does.
Example:
Create a module named myPropertySet. Therein put the following code:
option Compatible
option ClassModule
private aPropertyValues() as com.sun.star.beans.PropertyValue
public sub setProperty(oProp as com.sun.star.beans.PropertyValue)
bUpdated = false
for each oPropPresent in aPropertyValues
if oPropPresent.Name = oProp.Name then
oPropPresent.Value = oProp.Value
bUpdated = true
exit for
end if
next
if not bUpdated then
iIndex = ubound(aPropertyValues) + 1
redim preserve aPropertyValues(iIndex)
aPropertyValues(iIndex) = oProp
end if
end sub
public function getPropertyValue(sName as string) as variant
getPropertyValue = "N/A"
for each oProp in aPropertyValues
if oProp.Name = sName then
getPropertyValue = oProp.Value
exit for
end if
next
end function
Then within a standard module:
sub testClass
oPropertySet = new myPropertySet
dim prop as new com.sun.star.beans.PropertyValue
prop.Name = "blahblah1"
prop.Value = "blahblah1Value"
oPropertySet.setProperty(prop)
prop.Name = "blahblah2"
prop.Value = 3000
oPropertySet.setProperty(prop)
prop.Name = "blahblah3"
prop.Value = 1234.56
oPropertySet.setProperty(prop)
prop.Name = "blahblah2"
prop.Value = 8888
oPropertySet.setProperty(prop)
msgbox oPropertySet.getPropertyValue("blahblah1")
msgbox oPropertySet.getPropertyValue("blahblah2")
msgbox oPropertySet.getPropertyValue("blahblah3")
msgbox oPropertySet.getPropertyValue("blahblah4")
end sub
LibreOffice Basic supports the vb6 Collection type.
Dim coll As New Collection
coll.Add("blahblah1Value", "blahblah1")
coll.Add(3000, "blahblah2")
MsgBox(coll("blahblah1"))
Arrays of property values are the only thing that will work for certain UNO interfaces such as dispatcher calls. If you simply need a better way to deal with arrays of property values, then use a helper function.
Sub DisplayMyPropertyValue
Dim props(0 To 1) As New com.sun.star.beans.PropertyValue
props(0).Name = "blahblah1"
props(0).Value = "blahblah1Value"
props(1).Name = "blahblah2"
props(1).Name = 3000
MsgBox(GetPropertyByName(props, "blahblah1"))
End Sub
Function GetPropertyByName(props As Array, propname As String)
For Each prop In props
If prop.Name = propname Then
GetPropertyByName = prop.Value
Exit Function
End If
Next
GetPropertyByName = ""
End Function
XNameAccess is used for UNO containers such as Calc sheets. Normally these containers are obtained from the UNO interface, not created.
oSheet = ThisComponent.Sheets.getByName("Sheet1")
May UNO objects support the XPropertySet interface. Normally these are also obtained from the UNO interface, not created.
paraStyleName = cellcursor.getPropertyValue("ParaStyleName")
It may be possible to create a new class in Java that implements XPropertySet. However, Basic uses helper functions instead of class methods.
I think the serviceEnumerableMap is the answer (so far). Creating the values and searching them was much faster then creating props in a dynamic array and searching them with a for loop in basic.
(I do not "dare" to use "option Compatible", although I was a big fun of VB6 and VBA, because of the problems in code that maybe arise).
I used this code to test time in a form:
SUB testlala(Event)
TESTPROPS(Event)
' TESTENUM(Event)
MSGBOX "END OF TEST"
END SUB
SUB TESTENUM(Event)
DIM xcounter AS LONG
'b = now()
serviceEnumerableMap = com.sun.star.container.EnumerableMap
oEnumerableMap = serviceEnumerableMap.create("string", "any")
FOR xcounter= 0 TO 10000
oEnumerableMap.put("pr" & FORMAT(xcounter,"0000"), xcounter -10000)
NEXT
'b=now()-b
b = now()
FOR xcounter = 1 TO 5000
lala = Int((9000 * Rnd) +1)
g =oEnumerableMap.get("pr" & FORMAT(lala,"0000"))
'MSGBOX GetValueFromName(props,"pr" & FORMAT(xcounter,"0000"))
NEXT
b=now()-b
MSGBOX b*100000
END SUB
SUB TESTPROPS(Event)
DIM props()
DIM xcounter AS LONG
'b = now()
FOR xcounter= 0 TO 10000
AppendProperty(props,"pr" & FORMAT(xcounter,"0000"), xcounter -10000)
NEXT
'b=now()-b
b = now()
FOR xcounter = 1 TO 5000
lala = Int((9000 * Rnd) +1)
g = GetValueFromName(props,"pr" & FORMAT(lala,"0000"))
'MSGBOX GetValueFromName(props,"pr" & FORMAT(xcounter,"0000"))
NEXT
b=now()-b
MSGBOX b*100000
END SUB
REM FROM Andrew Pitonyak's OpenOffice Macro Information ------------------
Sub AppendToArray(oData(), ByVal x)
Dim iUB As Integer 'The upper bound of the array.
Dim iLB As Integer 'The lower bound of the array.
iUB = UBound(oData()) + 1
iLB = LBound(oData())
ReDim Preserve oData(iLB To iUB)
oData(iUB) = x
End Sub
Function CreateProperty(sName$, oValue) As com.sun.star.beans.PropertyValue
Dim oProperty As New com.sun.star.beans.PropertyValue
oProperty.Name = sName
oProperty.Value = oValue
CreateProperty() = oProperty
End Function
Sub AppendProperty(oProperties(), sName As String, ByVal oValue)
AppendToArray(oProperties(), CreateProperty(sName, oValue))
End Sub

How to accumulate running totals with constant amounts?

I am doing an assignment for my class and I'm a supeeeer beginner at coding. This assignment calls for me to select services and a discount rate and then show them in a total text box beneath the two.
My issue is, I'm supposed to be able to select multiple services and have them total together, but I just can't figure it out. I'll post the code below:
Public Class Form1
Dim CurrentServicesDec As Decimal
Dim CurrentServicesTotal As Decimal
Private DiscountServicesDec As Decimal
Private Sub CalculateButton_Click(sender As Object, e As EventArgs) Handles CalculateButton.Click
'Calculate the Service(s) selected and add the discount if any.
If MakeOverCheckBox.Checked = True Then
CurrentServicesDec = 125
ElseIf Val(HairStylingCheckBox.Checked) = True Then
CurrentServicesDec = 60
ElseIf Val(ManicureCheckBox.Checked) = True Then
CurrentServicesDec = 35
ElseIf Val(MakeupCheckbox.Checked) = True Then
CurrentServicesDec = 200
End If
If (TenPercentRadio.Checked) = True Then
DiscountServicesDec = 0.1
ElseIf TwentyPercentRadio.Checked = True Then
DiscountServicesDec = 0.2
End If
CurrentServicesTotal = CurrentServicesDec - (CurrentServicesDec * DiscountServicesDec)
txtTotal.Text = FormatCurrency(CurrentServicesTotal)
End Sub
Private Sub ClearButton_Click(sender As Object, e As EventArgs) Handles ClearButton.Click
'Clear the options on the form.
txtTotal.Text = ""
TenPercentRadio.Checked = False
TwentyPercentRadio.Checked = False
End Sub
Private Sub ExitButton_Click(sender As Object, e As EventArgs) Handles ExitButton.Click
'Close the program
Me.Close()
End Sub
Private Sub PrintButton_Click(sender As Object, e As EventArgs) Handles PrintButton.Click
'Print Preview the Form
PrintForm1.PrintAction = Printing.PrintAction.PrintToPreview
PrintForm1.Print()
End Sub
End Class
This is what I have, as far as the program being able to select one service and add the discount, it works perfectly but not for multiple services selected!
Thank you in advance.
Your program flow has some issues to get your intended behavior, since with your If block starting with If MakeOverCheckBox.Checked = True Then, using ElseIf for subsequent branches will cause them to skip.
You're also just assigning the value of CurrentServicesDec when I think you should be adding it together - so declare the variable inside your Calculate function and then use +=.
So to get the behavior of checking for multiples in your checkbox control - change the ElseIf to If blocks.
Private Sub CalculateButton_Click(sender As Object, e As EventArgs) Handles CalculateButton.Click
Dim CurrentServicesDec As Decimal
If MakeOverCheckBox.Checked = True Then
CurrentServicesDec += 125
End If
If HairStylingCheckBox.Checked = True Then
CurrentServicesDec += 60
End If
If ManicureCheckBox.Checked = True Then
CurrentServicesDec += 35
End If
If MakeupCheckbox.Checked = True Then
CurrentServicesDec += 200
End If

change BackColor on all forms Access VBA

I've been trying to change the design of all my forms. For this I've created a function which opens each form, performs the changes, then saves and closes it. I can change every control accessing it with ControlType, but I don't know how to access and perform changes on the backcolor of a form.
I've tried some ways but with no success, i tried
me.formheader.backcolor
or even
CurrentDb.Containers("forms").Documents(j).backcolor
I want to do this (which i did for one form):
Me.FormHeader.BackColor = RGB(225, 225, 255)
Me.FormFooter.BackColor = RGB(225, 225, 255)
Me.Detail.BackColor = RGB(242, 242, 242)
My code so far looks something like this: (it works for me)
Public Function SetFormDefaultsIleana()
Dim i, j As Integer
Dim wrkDefault As Workspace
Dim ctrl As Control
Dim frmName As String
Set wrkDefault = DBEngine.Workspaces(0)
For i = 0 To CurrentDb.Containers.Count - 1
If CurrentDb.Containers(i).Name = "Forms" Then
For j = 0 To CurrentDb.Containers("forms").Documents.Count - 1
frmName = CurrentDb.Containers("forms").Documents(j).Name
DoCmd.OpenForm frmName, acDesign
For Each ctrl In Forms(frmName)
If ctrl.ControlType = acLabel Then
DoCmd.SetWarnings False
ctrl.ForeColor = RGB(0, 0, 0)
'..
Next
DoCmd.Save acForm, frmName
DoCmd.Close acForm, frmName
Next j
End If
Next i
End Function
In the Forms module
me.Form.Section(*).BackColor = RGB(55,155,255)
and sections * are listed here

Monte Carlo Results in OpenOffice Calc

I have a simulation set up in OpenOffice. I want to display the results of the simulation, for say 100 replications, but I can't seem to work out how to do it. Obviously if you just copy the result to a cell and drag it down 100 rows, they all show the same figure.
Either a macro is necessary or there is a built in way. Neither of which I know.
Basically, the equivalent to http://www.youtube.com/watch?v=tpIhQuxQeNs
I created a macro as so:
Sub PasteSpecialNoFormula
dim args3(0) as new com.sun.star.beans.PropertyValue
args3(0).Name = "SelectedFormat"
args3(0).Value = 1
Dim document As Object
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
oDesktop = createUnoService("com.sun.star.frame.Desktop")
oDocument = ThisComponent
oSheet1 = oDocument.Sheets.getByIndex(0)
oSheet2 = oDocument.Sheets.getByIndex(0)
Dim i As Long, n As Long
n = 1000
for i = 1 to n
oFromRange = oSheet1.getCellRangeByName("B464:C464")
oToCell = oSheet2.getCellByPosition(1,466+i)
oDocument.CurrentController.Select(oFromRange)
dispatcher.executeDispatch(document, ".uno:Copy", "", 0, Array())
oDocument.CurrentController.Select(oToCell)
dispatcher.executeDispatch(document, ".uno:ClipboardFormatItems", "", 0, args3())
next i
End Sub