Conclusion : I give up :-)

We have three 'completed' approaches to the line numbering problem:

1. Jim Lambert's use of decimal-numbered-list style within the field (has the limitation of not allowing the user's text to include list styles, and some aesthetic constraints - but if you can live with those, IMHO it's by far the best solution)

2. Jim's earlier version using a separate text field with only the line numbers available. (Still has some aesthetic constraints, and noticeably slower than (1) above)

3. My styled text-based version using space-below in a separate field (works, no restrictions afaik, but *barely* fast enough for general use - and certainly too slow for many uses).

And then we have another, not yet complete, solution - use Hermann's script to find the limits on the line numbers which are visible, and then something lik emy 'spacebelow' to set just those lines - so it has the potential to be fast enough.

I set out to optimise the 'visibleLineNumber' function, and succeeded in getting it down to approx 20% of the original version, by:

 - reduce from 2 to 1 height calculations per iteration

 - convert from recursive to iterative

- use Newton-Raphson style linear interpolation, with tweaks to avoid the pathological end cases

- optimise the start values for finding the last visible line based on the effective field height and min allowed text size

I was just feeling good about that, and getting ready to start on the second half - displaying the line numbers for only those visible lines - when I got this email from Hermann re. the bug in formattedRect, and the need to use formattedHeight of chunk instead.

formattedHeight of line N to M of fld F has its own problems - it misses out the space needed for the last line if that line happens to be empty, and ignores spacebelow - but that can be easily avoided

function getAccurateFormattedHeight pFld, N, M

  put the formattedheight of line N to M of fld pFld into h

if line M of fld pFld is empty then add the formattedheight of line M of fld pFld to h

  add the spacebelow of line M of fld pFld to h

  return h

end getAccurateFormattedHeight


Then I started testing on "hard" fields - and only then did I discover the real problem - formattedHeight of a chunk is just too slow. It takes 50-100 msecs to get such a chunk from a large-ish field (30K lines, heavily styled, from the dictionary); so even though the number of iterations was down to between 7 and 12, that still represents over a second of elapsed time - which is just too long to be feasible.

And now that I have created this test case, it shows that solution no. 3 above is also too slow.

So, I give up :-)

-- Alex.

On 29/03/2017 11:48, hh via use-livecode wrote:
Alex,
before you waste valuable time:
The formattedRect can NOT be used in LC 7/8/9, because of the
(2^15 div 2)-limit for coordinates is active for that.

So the algorithm works in LC 6.7.11, but the results are sadly
'extremely' wrong in LC 7/8/9:
Crossing the 'limit' with a vertical coordinate jumps to negative
values for these coordinates. No chance to repair.

Using instead the formattedHeight of line 1 to N minus the
vscroll works. But it is not exactly doable if the last line is
empty (this is not added to the formattedHeight) = your space-below
problem -- a bug, TMHO.

I found another "dirty" but fast way to get pixel-exact line
positions for any field in LC 6/7/8/9. One could perhaps use it
until the text measuring problem is solved.

One needs a field that is transparent, is not threeD and has no
border (use instead an opaque rectangle with border as background).
Then set the hgrid to true (this has no visual effect if using as
borderColor the backColor of the graphic behind the field, for
testing I set the borderColor to red).
Now make a snapshot from the field and walk through the maskData
to search all such rows in the image that have a fully opaque
line. This is unique as long as the field has left and right
margins > 0.
This search in the image is *very fast* (< 2 ms), only taking the
snapshot and getting the maskdata needs some time (around 70 ms in
sum for 414x736 pixels).

A script that works here correctly with my tests is attached below.
It needs here in LC 9 about 120 ms for 4000 lines of heavily styled
text from the dictionary in a field of iPhone6 size (414x736).
Please change/improve/optimize it, especially the search part. This
is still the 'simple' binary search.

Hermann

You need a field "TEXT" (transparent etc, see above) and a
template-num-field "n00" (see my settings below as example).
Fields for feedback: "Range", "timing1", "timing2", "info"
===== [1] The field's script
on textchanged
  updateNbs2
end textchanged

on scrollbardrag
  updateNbs2
end scrollbardrag
===== [2] The numbering script (100 lines)
local nn="lineNumbers",l0,t0,b0,w0,h0,sw0,v0
local rg="TEXT", fw=32 -- num-field width

on updateNbs2
  lock screen; lock messages
  put the millisecs into m0 ---- start
  put the top of fld rg into t0
  put the bottom of fld rg into b0
  put the left of fld rg into l0
  put the width of fld rg into w0
  put the height of fld rg into h0
  put the vscroll of fld rg into v0
  put the scrollbarwidth of fld rg into sw0
  set the properties of the templatefield to \
      the properties of fld "n00" -- see below
  if there is a grp nn then delete grp nn
  create grp nn
  set lockloc of grp nn to true
  set opaque of grp nn to true
  set backColor of grp nn to "204,204,204"
  set lockloc of grp nn to true
  set width of grp nn to fw
  set height of grp nn to the height of fld "text"
  set topleft of grp nn to l0-fw, t0
  set layer of grp nn to 1
  put the millisecs into m1 ---- diff1
  put visibleTextLines()-1 into tL
  put the millisecs into m2 ---- diff2
  put getLocs() into g
  put the millisecs into m3 ---- diff3
  put "Lines: " & (tL,tL-1+the num of lines of g) \
       into fld "range"
  repeat for each line L in g
    put item 2 of L into i
    put ("n"&i) into ni
    if there is a fld ni then delete fld ni
    create fld ni in grp nn
    set left of fld ni to l0-fw+2
    set top of fld ni to i-10
    put tL into fld ni
    add 1 to tL
  end repeat
  reset the templatefield
  put the millisecs into m4 ---- diff4
  put (m4-m0 &"="& m2-m1 &"+"& m3-m2 &"+"& m4-m3+m1-m0) & \
      " ms" into fld "timing"
  unlock screen; unlock messages
end updateNbs2

function getLocs
  put the millisecs into m0 --------------------- start
  set topleft of img rg to \
      (5+the right of fld rg, the top of fld rg)
  put w0-sw0 into w1
  put the millisecs into m1 --------------------- diff1
  export snapshot from fld rg to img rg as PNG
  put the millisecs into m2 --------------------- diff2
  put the maskdata of img rg into mData
  put the millisecs into m3 --------------------- diff3
  put NumToByte(255) into c1
  repeat 1+log2(w1)
    put c1 after c1
  end repeat
  put byte 1 to w1 of c1 into c00
  put the millisecs into m4 --------------------- diff4
  repeat with i=1 to h0
    put (i-1)*w0 into i0
    if byte i0+1 to i0+w1 of mData is c00
    then put cr&(0,t0+i) after s
  end repeat
  -- avoid overlapping:
  -- put (0,min(t0,10+item 2 of line 1 of s)) before s
  put cr & (0,max(h0+t0,10+item 2 of line -1 of s)) after s
  put s into fld "info"
  put the millisecs into m5 --------------------- diff5
  put (m5-m0 &"="& m2-m1 &"+"& m3-m2 &"+"& m5-m3+m1-m0) & \
        " ms" into fld "timing2"
  return char 2 to -1 of s
end getLocs

function visibleTextLines
  lock screen; lock messages
  put the selectedChunk into sc
  put the scrollbarWidth of fld rg into sw
  put the margins of fld rg into m
  put (m,m,m,m) into m -- now we have at least 4 items
  put the num of lines of fld rg into n
  put findTopLine(v0+t0-1+item 4 of m,1,n) into L1
  return L1-1
end visibleTextLines

function findTopLine x,n1,n -- x=top pixel, n1=start, n=max
  put n1+((n-n1) div 2) into m
  if (the formattedHeight of line 1 to (m+1) of fld rg) >= x then
    if (the formattedHeight of line 1 to m of fld rg) < x then
      return m+1
    else
      if m <= n1 then return n1
      else return findTopLine(x,n1,m-1)
    end if
  else
    if m >= n then return n
    else return findTopLine(x,m+1,n)
  end if
end findTopLine

===== [3] This may be used for the "template"-num-fld "n00"
--   set locktext of fld "n00" to true
--   set dontwrap of fld "n00" to true
--   set traversalOn of fld "n00" to false
--   set height of fld "n00" to 12
--   set width of fld "n00" to fw
--   set margins of fld "n00" to 4
--   set textalign of fld "n00" to "right"
--   set opaque of fld "n00" to false
--   set threed of fld "n00" to false
--   set showborder of fld "n00" to false
--   set textsize of fld "n00" to 10
--   set foreColor of fld "n00" to "0,0,255"
=====END_OF_SCRIPT


_______________________________________________
use-livecode mailing list
use-livecode@lists.runrev.com
Please visit this url to subscribe, unsubscribe and manage your subscription 
preferences:
http://lists.runrev.com/mailman/listinfo/use-livecode


_______________________________________________
use-livecode mailing list
use-livecode@lists.runrev.com
Please visit this url to subscribe, unsubscribe and manage your subscription 
preferences:
http://lists.runrev.com/mailman/listinfo/use-livecode

Reply via email to