I have big problems with layouting dynamic generated Figures with figures in figures.
I expect the following:
If I specifiy size() for a figure the figure will never get smaller. This is in the contract of size property.
if I do pack it will rearrange the figures without making them smaller than specified size(). It will 'rearrange and draw a box arround it' (in terms that it is a new figure)
If the whole figure cannot be displayed in the Eclipse window I get automatically (outer)scrollbars.
So if I execute this:
public void tester() {
list[Figure] list1 = [];
for ( i <- [0..3]) {
list[Figure] list2 = [];
list2 = list2 + box(size(30,200),fillColor("brown"));
list2 = list2 + box(size(50,610),fillColor("black"));
list2 = list2 + box(size(100,310),fillColor("gray"));
list1 = list1 + pack(list2,gap(10),fillColor("red"));
};
render(pack(list1,gap(10),fillColor("blue")));
}
I expect something like this (or layout vertically I do not care), and if there is not enough room I expect scrollbars under and right of the figure:
However what I get is the following. But this is not what I asked for. I did not ask for individual scrollbars (I want outer scrollbars if needed). Also the blue and red border (because of gap) is not visible.
How do I get this more in line with what I expected, and why is it behaving as it is?
The critical issue seems to be that a size specification is missing in the inner pack. If you rewrite your code as follows things look much better:
public void tester() {
list[Figure] list1 = [];
for ( i <- [0..3]) {
list[Figure] list2 = [];
list2 = list2 + box(size(30,200),fillColor("brown"));
list2 = list2 + box(size(50,610),fillColor("black"));
list2 = list2 + box(size(100,310),fillColor("gray"));
list1 = list1 + pack(list2,gap(10),size(200,500),fillColor("red"));
};
render(pack(list1,gap(10),fillColor("blue")));
}
The resulting figure is as follows (note that all packs have a scrollbar and the right one has been scrolled down a bit):
As a side note: the current handling of sizes and resizing is really confusing and we are working on a better solution.
I will go for a different visual presentation (came up with some other ideas). I played a few hours with pack-in-pack, and I still believe that packs-inside-packs are not correctly handled.
Jos
Related
New to Kotlin, working on a simple chain of circles. I have been able to get two circles to connect the way I want but can seem to grow the chain further. Seems like the width (w2) doesn't get updated after the first iteration. Let me know why my code isn't working and how I can improve it.
Thank you in advance :) Stay woke!
val iterator = (0..12).iterator()
if (iterator.hasNext()) {
canvas.drawCircle(w.toFloat(), h.toFloat(), (100).toFloat(),brush1)
iterator.next()
}
iterator.forEach {
val w2 = w-100
canvas.drawCircle((w2).toFloat(), h.toFloat(), (100).toFloat(),brush1)
}
here is the kind of effect I'm looking to create
w2 will never change because it's based on w which is never modified.
You can use parameter provided to lambda (it) which tells you what iteration you're on, and not use weird iterator:
val x = 100 // starting x
val inc = 100 // offset for following circles
repeat(12){
val targetX = x + inc * it
canvas.drawCircle(targetX.toFloat(), y.toFloat(), 100.toFloat(), brush)
}
I am trying to implement RGB histogram computation for images in Swift (I am new to iOS).
However the computation time for 1500x1000 image is about 66 sec, which I consider to be too slow.
Are there any ways to speed up image traversal?
P.S. current code is the following:
func calcHistogram(image: UIImage) {
let bins: Int = 20;
let width = Int(image.size.width);
let height = Int(image.size.height);
let binStep: Double = Double(bins-1)/255.0
var hist = Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Int())))
for i in 0..<bins {
for j in 0..<bins {
for k in 0..<bins {
hist[i][j][k] = 0;
}
}
}
var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
var data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
for x in 0..<width {
for y in 0..<height {
var pixelInfo: Int = ((width * y) + x) * 4
var r = Double(data[pixelInfo])
var g = Double(data[pixelInfo+1])
var b = Double(data[pixelInfo+2])
let r_bin: Int = Int(floor(r*binStep));
let g_bin: Int = Int(floor(g*binStep));
let b_bin: Int = Int(floor(b*binStep));
hist[r_bin][g_bin][b_bin] += 1;
}
}
}
As noted in my comment on the question, there are some things you might rethink before you even try to optimize this code.
But even if you do move to a better overall solution like GPU-based histogramming, a library, or both... There are some Swift pitfalls you're falling into here that are good to talk about so you don't run into them elsewhere.
First, this code:
var hist = Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Int())))
for i in 0..<bins {
for j in 0..<bins {
for k in 0..<bins {
hist[i][j][k] = 0;
}
}
}
... is initializing every member of your 3D array twice, with the same result. Int() produces a value of zero, so you could leave out the triple for loop. (And possibly change Int() to 0 in your innermost repeatedValue: parameter to make it more readable.)
Second, arrays in Swift are copy-on-write, but this optimization can break down in multidimensional arrays: changing an element of a nested array can cause the entire nested array to be rewritten instead of just the one element. Multiply that by the depth of nested arrays and number of element writes you have going on in a double for loop and... it's not pretty.
Unless there's a reason your bins need to be organized this way, I'd recommend finding a different data structure for them. Three separate arrays? One Int array where index i is red, i + 1 is green, and i + 2 is blue? One array of a custom struct you define that has separate r, g, and b members? See what conceptually fits with your tastes or the rest of your app, and profile to make sure it works well.
Finally, some Swift style points:
pixelInfo, r, g, and b in your second loop don't change. Use let, not var, and the optimizer will thank you.
Declaring and initializing something like let foo: Int = Int(whatever) is redundant. Some people like having all their variables/constants explicitly typed, but it does make your code a tad less readable and harder to refactor.
Int(floor(x)) is redundant — conversion to integer always takes the floor.
If you have some issues about performance in your code, first of all, use Time Profiler from Instruments. You can start it via Xcode menu Build->Profile, then, Instruments app opened, where you can choose Time Profiler.
Start recording and do all interactions in the your app.
Stop recording and analyse where is the "tightest" place of your code.
Also check options "Invert call tree", "Hide missing symbols" and "Hide system libraries" for better viewing profile results.
You can also double click at any listed function to view it in code and seeing percents of usage
I am trying to somehow replicate the range bar chart here.
I've found this reference but I don't fully grasp the code.
What I have is a series of task (sometimes accomplished in different periods).
let d = [("task1", DateTime.Parse("11/01/2014 08:30"), DateTime.Parse("12/01/2014 10:30"));
("task2", DateTime.Parse("15/01/2014 09:30"), DateTime.Parse("16/01/2014 10:30"));
("task3", DateTime.Parse("11/01/2014 08:30"), DateTime.Parse("16/01/2014 10:30"))]
let chart = d |> FSharp.Charting.Chart.RangeBar
chart.ShowChart()
I am struggling to understand the logic of the API.
I have also tried:
let chart = new Windows.Forms.DataVisualization.Charting.Chart(Dock = DockStyle.Fill)
let area = new ChartArea("Main")
chart.ChartAreas.Add(area)
let mainForm = new Form(Visible = true, TopMost = true, Width = 700, Height = 500)
mainForm.Controls.Add(chart)
let seriesColumns = new Series("NameOfTheSerie")
seriesColumns.ChartType <- SeriesChartType.RangeBar
type SupportToChart(serieVals: Series) =
member this.addPointXY(lbl, [<ParamArray>] yVals: Object[]) =
serieVals.Points.AddXY(lbl, yVals) |> ignore
let supporter = SupportToChart(seriesColumns)
supporter.addPointXY("AAA", DateTime.Parse("11/01/2014 08:30"), DateTime.Parse("12/01/2014 10:30") )
which results in
System.ArgumentOutOfRangeException: You can only set 1 Y values for
this data point.
Has something changed in the API since then?
I'm not entirely sure that F# Charting is currently powerful enough to be able to reconstruct the above chart. However, one of the problems seems to be that it treats dates as float values (for some reason) and incorrectly guesses the ranges. You can at least see the chart if you use:
Chart.RangeBar(d)
|> Chart.WithYAxis(Min=41650.0, Max=41660.0)
Please submit this as an issue on GitHub. If you want to dig deeper into how F# Charting works and help us get this fixed, that would be amazing :-)
The trick is initializing the Series with
let serie = new Series("Range", yValues)
where yValues defines the max number of "Y-values".
Hi i have problem with async drawing on Panel. If i dont clear it works but with clear it is flickering all time long. I create Form and Panel in main loop but I want to draw in game loop. My problem is how to draw on Graphics panel without having flickering
let rec gameLoop (gamePanel:Panel) (lastTime:int64) (ball:Ball) = async {
lock gamePanel ( fun() ->
if gamePanel.IsDisposed || close then
()
else
let rectangle = new System.Drawing.Rectangle(100, 100, 200, 200);
use graphics = gamePanel.CreateGraphics();
graphics.Clear(Color.White) (* when i use this it flickers *)
graphics.DrawEllipse(System.Drawing.Pens.Black, rectangle);
graphics.FillEllipse(ball.brush(), ball.rectangle());
)
return! gameLoop gamePanel (lastTime) (ball.move())
}
edit x2. Flickering still exists but maybe i am on good way
type PanelF() as this=
inherit Panel()
do this.DoubleBuffered <- true
member this.setStyle(a,b) = this.SetStyle(a, b)
edit x3. I added System.Threading.Thread.Sleep(30) it works better but still not quite what i wanted
Do
do this.DoubleBUffered<-true
In the constructor of your form. This will enable double buffering which stops flickering by rendering to a hidden buffer and swapping buffers once rendering is done.
Here is a full answer in the idiomatic F# style (using class ... end is not common`)
type PanelF() as this=
inherit Panel()
do this.DoubleBuffered <- true
member this.setStyle(a,b) = this.SetStyle(a, b)
So I am working on a project using F# for some SVG line manipulations.
I thought it would be good to represent color an RGB value as a tuple (R,G,B). It just made sense to me. Well since my project involves generating SVG lines in a loop. I decided to have a color offset, conveniently also represented in a tuple (Roffset, Goffset, Boffset)
An offset in this case represents how much each line differs from the previous.
I got to a point where I needed to add the tuples. I thought since they were of the same dimensions and types, it would be fine. But apparently not. I also checked the MSDN on tuples, but I did not find anything about how to add them or combine them.
Here is what I tried. Bear in mind I tried to omit as much irrelevant code as possible since this is a long class definition with LOTS of members.
type lineSet ( 10+ params omitted ,count, colorOff :byte*byte*byte, color :byte*byte*byte ,strokeWid , strokeWidthOff ) =
member val Color = color with get, set
member val ColorOffset = colorOff with get, set
member val lineCount = count with get, set
interface DrawingInterfaces.IRepresentable_SVG with
member __.getSVGRepresenation() =
let mutable currentColor = __.Color
for i in 1..__.lineCount do
currentColor <- currentColor + __.ColorOffset
That last line of code is what I wanted to do. However, it appears you cannot add tuples directly.
I also need a way to clamp the result so it cannot go over 255, but I suspect a simple try with block will do the trick. OR I could let the params take a type int*int*int and just use an if to reset it back to 255 each time.
As I mentioned in the comments, the clamping function in your code does not actually work - you need to convert the numbers to integers before doing the addition (and then you can check if the integer is greater than 255). You can do something like this:
let addClamp (a:byte) (b:byte) =
let r = int a + int b
if r > 255 then 255uy else byte r
Also, if you work with colors, then it might make sense to define a custom color type rather than passing colors around as tuples. That way, you can also define + on colors (with clamping) and it will make your code simpler (but still, 10 constructor arguments is a bit scary, so I'd try to think if there is a way to simplify that a bit). A color type might look like this:
type Color(r:byte, g:byte, b:byte) =
static let addClamp (a:byte) (b:byte) =
let r = int a + int b
if r > 255 then 255uy else byte r
member x.R = r
member x.B = b
member x.G = g
static member (+) (c1:Color, c2:Color) =
Color(addClamp c1.R c2.R, addClamp c1.G c2.G,addClamp c1.B c2.B)
Using the type, you can then add colors pretty easily and do not have to add clamping each time you need to do that. For example:
Color(255uy, 0uy, 0uy) + Color(1uy, 0uy, 0uy)
But I still think you could make the code more readable and more composable by refactoring some of the visual properties (like stroke & color) to a separate type and then just pass that to LineSet. This way you won't have 10+ parameters to a constructor and your code will probably be more flexible too.
Here is a modified version of your code which I think is a bit nicer
let add3DbyteTuples (tuple1:byte*byte*byte , tuple2:byte*byte*byte) =
let inline intify (a,b,c) = int a,int b,int c
let inline tripleadd (a,b,c) (d,e,f) = a+d,b+e,c+f
let clamp a = if a > 255 then 255 else a
let R,G,B = tripleadd (intify tuple1) (intify tuple2)
clamp R,clamp G,clamp B