New version of Trackeditor + Height Tool

Discuss or share your track-related work here

Moderator: kilo

New version of Trackeditor + Height Tool

Postby simon-2 » Tue Dec 25, 2018 1:51 pm

Hi guys-

please don't expect too much from the subject title- essentially, it is still the same old java editor with its limitations (especially elevations!! so don't use it after definition of elevations).

But- there is one new feature you might also like: "Subdivide Segment".
With this, you can split any segment (including clothoid curves) at any point, not affecting the track's alignment. So you can achieve perfect alignment of your track with the image source and align the first and last segment in the very first step- and then subsequently split it into smaller segments to incorporate changes in texture, elevation, side width, border shape, ...

short demo:
both curves were 180deg before, with radii from 30m to 200m- now the first curve has been split. Although the image might suggest it would have been split in the middle, this is not the case- it is always split on click position (bad demo).
preview2.png


Remember: this thing will still destroy heights (and more)! So you should only use it before you start editing the xml file, setting up elevations and more. If you need to split a segment later, you can still open it in track editor, split the segment, copy the values for arc and radii to your xml and close track editor without saving.

Hope you enjoy!
regards (and merry christmas, btw :) )
Simon
You do not have the required permissions to view the files attached to this post.
Last edited by simon-2 on Wed Dec 26, 2018 4:52 pm, edited 1 time in total.
simon-2
 
Posts: 89
Joined: Thu Jan 29, 2015 3:01 pm

Re: New version of Trackeditor!

Postby simon-2 » Wed Dec 26, 2018 4:51 pm

next one is a simple python script i made to create elevation data.

usage is simple, but is has some bugs and limitations, so please read the following instructions carefully. I also recommend to work on a copy, as there is no [ctrl]+[z] ;)

file needs to be prepared:
-all "z start" values, except for that of the very first segment, must be deleted from the xml before you start the tool (ctrl+h, replace with nothing).
-the tool itself does only interpolation and smoothing, so you need to define some initial elevations in the xml file. Enter the "z end" values for some key segments and leave the remaining segments at 0.0m (or whatever).
-key elevations must not be zero! Also define the first segment's z start value <> 0
-the xml interface doesn't handle the &default-surfaces and similar inclusions, so you need to remove them.
-comments in the xml do not disturb the script from working, but are not written to the output- so either back it up and copy/paste it to the output, or avoid using comments before you finished your elevations ;-)

now, start the script typing "python heighttool.py" within the appropriate folder and the script will tell you what to enter:
-enter category and name of the track to be edited (if the file is not found, check/edit the path in line 30 of the script)

-now, step through the track:
-for each editing step, you select a section of the track, defined by two of your "key segments", and the script will calculate smooth elevation data for all segments within this section. A camber value is also included, which is the maximum deviation from a linear ramp. For camber value other than zero, a quadratic-curved crest or through (or mountain/valley for longer sections) is constructed.

-you can also use calculated values from the previous steps, i.e. you can start with very few key segments and subsequently smooth and camber through arbitrary sub-sections.

-enter x after your last editing step to leave the edit loop

-now, you can run additional smoothing algorithms over the complete track:
-height smoothing iterations: this one smooths the z end values, so don't exaggerate or your elevations will be bulldozed, but 1-3 iterations might help to improve the transitions
-"smooth tangents" has a very cheap algorithm, so i rather recommend to use
-"smooth tangents (parabolic)". This calculates appropriate values for each segment's "profil end tangent" according to the elevation values.

et voila :) - finished.

attachment quota has been reached, so copy/paste the following code to an editor and save it as python file :roll:

Code: Select all
import sys
import traceback
import math
#try:
#  from lxml import etree
#  print("running with lxml.etree")
#except ImportError:
try:
    # Python 2.5
  import xml.etree.cElementTree as etree
  print("running with cElementTree on Python 2.5+")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.ElementTree as etree
    print("running with ElementTree on Python 2.5+")
  except ImportError:
    try:
      # normal cElementTree install
      import cElementTree as etree
      print("running with cElementTree")
    except ImportError:
      try:
        # normal ElementTree install
        import elementtree.ElementTree as etree
        print("running with ElementTree")
      except ImportError:
        print("Failed to import ElementTree from any known place")
#enter path (relative to location of this file) for data directory
path = "/usr/share/games/speed-dreams-2/tracks"
#
gotfile = 0
while (gotfile < 1):
  category = raw_input("Enter track category (or x to exit): ")
  if ( category == "x" ): break
  name = raw_input("Enter Name of Track to be edited (or x to exit): ")
  if ( name == "x" ): break
  path1 = path + '/' + category + '/' + name
  fullpath = path1 + '/' + name + '.xml'
  print("Opening file %s..." %fullpath)
  try:
    xmlfile = etree.parse(fullpath)
    root = xmlfile.getroot()
    print("successfully opened file")
    break
  except:
    print("could not open file")
    exc_type, exc_value, exc_traceback = sys.exc_info()
    lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
    print ''.join('!! ' + line for line in lines)
for section in root:
  if section.attrib['name']=="Main Track":
     print ('Main Track section found')
     for section2 in section:
        if section2.attrib['name']=="Track Segments":
          tracksegs = section2
          print("Track Segments found")
editing =1
while editing == 1:
  #get index of first and last segment
  intervalstart = raw_input("Name of first segment to smooth (or x to exit)? ")
  intervalend = raw_input("Name of last segment to smooth (or x to exit)? ")
  for segment in tracksegs:
    if segment.attrib['name'] == intervalstart: firstseg = tracksegs.getchildren().index(segment)
    if segment.attrib['name'] == intervalend: lastseg=tracksegs.getchildren().index(segment)
 
  #lookup start and end height
  firstzstart=0
  lastzend=0
  zend=[]
  #start height
  segment=tracksegs[firstseg]
  for att in segment:       
          if att.attrib['name']=="z start":
              firstzstart=float(att.attrib['val'])
  if firstzstart == 0:
      for att2 in tracksegs[(firstseg-1)]:
          if att2.attrib['name']=="z end":
              firstzstart=float(att2.attrib['val'])
  if firstzstart == 0:         
      print "no z found; do it the long way..."                   
      cirqzstart=0
      try:
          cirqzstart=tracksegs[0].find("./*[@name='z start']").attrib['val']
      except:
          cirqzstart=0
      oldzend=cirqzstart
      try:
          oldgrade=tracksegs[0].find("./*[@name='profil start tangent']").attrib['val']
      except:
          oldgrade=0   
      i=0
      currentzstart=cirqzstart
      while i < firstseg:
          segment=tracksegs[i]
          #print 'processing segment ' + segment.attrib['name']
          try:
              currentzstart=segment.find("./*[@name=''z start]").attrib['val']
          except:
              #print "no z start found"
              currentzstart=oldzend
          try:
              zend[i]=segment.find("./*[@name='z zend']").attrib['val']
          except:
              #print "no z end found in previous section"
              try:
                  lg=segment.find("./*[@name='lg']").attrib['val']
              except:
                  arc=segment.find("./*[@name='arc']").attrib['val']
                  radius=segment.find("./*[@name='radius']").attrib['val']
                  try:
                      endradius=segment.find("./*[@name='end radius']").attrib['val']
                  except:
                      endradius=radius
                  lg=float((float(radius)+float(endradius))/2*float(arc)*math.pi/180)
              #print 'length: '+str(lg)     
              grade=0
              try:
                  grade=segment.find("./*[@name='grade']").attrib['val']
              except:
                  try:
                      grade=tracksegs[i-1].find("./*[@name='profil end tangent']").attrib['val']
                  except:
                      grade=oldgrade
              #print 'grade:'+grade       
              zend.insert(i, float(float(currentzstart)+float(lg)*float(grade)/100))
          oldzend=zend[i]
          #print 'zend: ' + str(zend[i])
          oldgrade=grade
          i+=1
      #end while     
      try:
          firstzstart=zend[str(int(firstseg)-1)]
      except:
          firstzstart=currentzstart
         
  print "start z of interval: " + str(firstzstart)
  #
  #end height height
  #
 
  segment=tracksegs[lastseg]
  for att in segment:       
          if att.attrib['name']=="z end":
              lastzend=float(att.attrib['val'])
  if lastzend == 0:         
      print "no z end found; do it the long way..."                   
     
      oldzend=firstzstart
      i=firstseg
      try:
          oldgrade=tracksegs[i].find("./*[@name='profil start tangent']").attrib['val']
      except:
          oldgrade=0
          k=i
          while oldgrade==0:
              try:
                  oldgrade=tracksegs[k].find("./*[@name='profil end tangent']").attrib['val']
              except:
                  try:
                      oldgrade=tracksegs[k].find("./*[@name='grade']").attrib['val']
                  except:
                      oldgrade=0
              if k<0:
                  oldgrade = 0
                  break
              k-=1
              #end while
      currentzstart=firstzstart
      while i < lastseg+1:
          segment=tracksegs[i]
          #print 'processing segment ' + segment.attrib['name']
          try:
              currentzstart=segment.find("./*[@name=''z start]").attrib['val']
          except:
              #print "no z start found"
              currentzstart=oldzend
             
          try:
              zend[i]=segment.find("./*[@name='z zend']").attrib['val']
          except:
              #print "no z end found in previous section"
              try:
                  lg=segment.find("./*[@name='lg']").attrib['val']
              except:
                  arc=segment.find("./*[@name='arc']").attrib['val']
                  radius=segment.find("./*[@name='radius']").attrib['val']
                  try:
                      endradius=segment.find("./*[@name='end radius']").attrib['val']
                  except:
                      endradius=radius
                  lg=float((float(radius)+float(endradius))/2*float(arc)*math.pi/180)
              #print 'length: '+str(lg)     
              grade=0
              try:
                  grade=segment.find("./*[@name='grade']").attrib['val']
              except:
                  try:
                      grade=tracksegs[i].find("./*[@name='profil end tangent']").attrib['val']
                  except:
                      grade=oldgrade
              #print 'grade:'+grade       
              zend.insert(i, float(float(currentzstart)+float(lg)*float(grade)/100))
          oldzend=zend[i]
          #print 'zend: ' + str(zend[i])
          oldgrade=grade
          i+=1
      #end while     
      try:
          lastzend=zend[str(int(lastseg)-1)]
      except:
          lastzend=currentzstart
         
  print "end z of interval: " + str(lastzend)
  camber = float(raw_input("camber in meters (>0 crest, <0 through, 0 linear)? "))
  print 'smooting from seg #' + str(firstseg) + ' to #' + str(lastseg) + '...'
  if camber == "0":
      print 'initialize linear smoother...'
  else:
      print 'initialize quadratic smoother...'
 
 
  i=firstseg
 
  lgges=0
  lgx = []
  while i<(lastseg+1):
      segment = tracksegs[i]
      radius = 0
      endradius = 0
      zstart=-1000
      zend=-1000
      grade=-1000
      lg=0
      for att in segment:
          if att.attrib['name']=="type": typestr=att.attrib['val']
          if att.attrib['name']=="lg": lg=float(att.attrib['val'])
          if att.attrib['name']=="z start": zstart=float(att.attrib['val'])
          if att.attrib['name']=="z end": zend=float(att.attrib['val'])
          if att.attrib['name']=="arc": arc=float(att.attrib['val'])
          if att.attrib['name']=="radius": radius=float(att.attrib['val'])
          if att.attrib['name']=="end radius": endradius=float(att.attrib['val'])
          if att.attrib['name']=="grade": grade=float(att.attrib['val'])
      if endradius==0: endradius = radius
      if lg==0: lg=arc*math.pi/180*(radius+endradius)/2
      lgges+=lg
     
      lgx.insert(i, lgges)
     
      i+=1
  #print lgx
  print 'length to be smoothed: ' + str(lgges)
  deltah = lastzend - firstzstart
  print 'height difference: ' + str(deltah)
  grade = deltah/lgges*100
  print 'grade: ' + str(grade)
  i=firstseg
  x=[]
  newzend=firstzstart
  newendtangent=grade
 
  while i<(lastseg+1): #find actual height for z end tangent
      j=i-firstseg
      segment = tracksegs[i]
      #print lgx[j]
      x.insert(j, lgx[j]/lgges*2-1)
      #print x[j]
      newzend=firstzstart+grade*lgx[j]/100
      newendtangent=grade
      if not camber == "0": #for camber:
          newzend+=camber
          newzend-=camber*x[j]*x[j]
          newendtangent-=x[j]*2*camber/100
      #write to tree here
      try:
          segment.find("./*[@name='z end']").attrib['val'] = str(newzend)
      except:
          #create attribute
          etree.SubElement(segment, "attnum", name="z end",unit="m", val=str(newzend))
      try:
          segment.find("./*[@name='profil end tangent']").attrib['val'] = str(newendtangent)
      except:
          #create attribute
          etree.SubElement(segment, "attnum", name="profil end tangent", unit="%", val=str(newendtangent))
      #remove grade
      if not segment.find("./*[@name='grade']") == None:
          segment.remove(segment.find("./*[@name='grade']"))
     
      i+=1
      #end while
 
  prompt = raw_input("enter 'x' if finished editing ")
  if prompt == "x":
    editing = 0
 
   
   
#end smoothing step
#heightsmoother
smoothheights=int(raw_input("enter number of height smoothing iterations "))
while smoothheights > 0:
  numberofsegs=len(tracksegs.findall('section'))
  i=0
  while i<(numberofsegs-1):
    segment = tracksegs[i]
    nextseg = tracksegs[i+1]
    try:
      length1=float(segment.find("./*[@name='lg']").attrib['val'])
    except:
      arc=segment.find("./*[@name='arc']").attrib['val']
      radius=segment.find("./*[@name='radius']").attrib['val']
      try:
        endradius=segment.find("./*[@name='end radius']").attrib['val']
      except:
        endradius=radius
      length1=float((float(radius)+float(endradius))/2*float(arc)*math.pi/180)   
    try:
      length2=float(nextseg.find("./*[@name='lg']").attrib['val'])
    except:
      arc=nextseg.find("./*[@name='arc']").attrib['val']
      radius=nextseg.find("./*[@name='radius']").attrib['val']
      try:
        endradius=nextseg.find("./*[@name='end radius']").attrib['val']
      except:
        endradius=radius
      length2=float((float(radius)+float(endradius))/2*float(arc)*math.pi/180)
    try:
      z0=float(segment.find("./*[@name='z start']").attrib['val'])
    except:
      try:
        z0=float(tracksegs[i-1].find("./*[@name='z end']").attrib['val'])
      except:
        z0=(float(tracksegs[i-1].find("./*[@name='z end left']").attrib['val'])+float(tracksegs[i-1].find("./*[@name='z end right']").attrib['val']))/2
    try:
      z1=float(segment.find("./*[@name='z end']").attrib['val'])
    except:
      z1=(float(segment.find("./*[@name='z end left']").attrib['val'])+float(segment.find("./*[@name='z end right']").attrib['val']))/2
    try:
      z2=float(nextseg.find("./*[@name='z end']").attrib['val'])
    except:
      z2=(float(nextseg.find("./*[@name='z end left']").attrib['val'])+float(nextseg.find("./*[@name='z end right']").attrib['val']))/2
   
    l1=float(length1)
    l2=float(length2)
   
    z1new=((z0+(z2-z0)/(l1+l2)*l1)+3*z1)/4
   
   
    try:
      segment.find("./*[@name='z end']").attrib['val'] = str(z1new)
    except:
      #create attribute
      print 'track segment ' + segment.attrib['name'] + ' has no single z end'
    i+=1
 
  smoothheights-=1
  #end heightsmoother

     
#smooth all tangents
smoothtang= raw_input("smooth tangents (y/n)?")
if smoothtang == "y":
  print tracksegs.findall('section')
  numberofsegs=len(tracksegs.findall('section'))
  i=0
  while i<(numberofsegs-1):
    segment = tracksegs[i]
    nextseg = tracksegs[i+1]
    try:
      length1=float(segment.find("./*[@name='lg']").attrib['val'])
    except:
      arc=segment.find("./*[@name='arc']").attrib['val']
      radius=segment.find("./*[@name='radius']").attrib['val']
      try:
        endradius=segment.find("./*[@name='end radius']").attrib['val']
      except:
        endradius=radius
      length1=float((float(radius)+float(endradius))/2*float(arc)*math.pi/180)   
    try:
      length2=float(nextseg.find("./*[@name='lg']").attrib['val'])
    except:
      arc=nextseg.find("./*[@name='arc']").attrib['val']
      radius=nextseg.find("./*[@name='radius']").attrib['val']
      try:
        endradius=nextseg.find("./*[@name='end radius']").attrib['val']
      except:
        endradius=radius
      length2=float((float(radius)+float(endradius))/2*float(arc)*math.pi/180)
    try:
      z0=segment.find("./*[@name='z start']").attrib['val']
    except:
      try:
        z0=tracksegs[i-1].find("./*[@name='z end']").attrib['val']
      except:
        z0=(float(tracksegs[i-1].find("./*[@name='z end left']").attrib['val'])+float(tracksegs[i-1].find("./*[@name='z end right']").attrib['val']))/2
    try:
      z1=segment.find("./*[@name='z end']").attrib['val']
    except:
      z1=(float(segment.find("./*[@name='z end left']").attrib['val'])+float(segment.find("./*[@name='z end right']").attrib['val']))/2
    try:
      z2=nextseg.find("./*[@name='z end']").attrib['val']
    except:
      z2=(float(nextseg.find("./*[@name='z end left']").attrib['val'])+float(nextseg.find("./*[@name='z end right']").attrib['val']))/2
    grade1=float((float(z1)-float(z0))/float(length1))
    grade2=float((float(z2)-float(z1))/float(length2))
   
    endtangent=(grade1*length2+grade2*length1)/(2*(length1+length2))*100
   
    print segment.attrib['name'] + 'grade1: ' + str(grade1)+ ', grade2: ' + str(grade2) + ', endtangent: ' + str(endtangent)
    try:
      segment.find("./*[@name='profil end tangent']").attrib['val'] = str(endtangent)
    except:
      #create attribute
      etree.SubElement(segment, "attnum", name="profil end tangent", unit="%", val=str(endtangent))
    i+=1
   
#smooth all tangents second version
smoothtang2= raw_input("smooth tangents (parabolic) (y/n)?")
if smoothtang2 == "y":
  print tracksegs.findall('section')
  numberofsegs=len(tracksegs.findall('section'))
  i=0
  while i<(numberofsegs-1):
    segment = tracksegs[i]
    nextseg = tracksegs[i+1]
    try:
      length1=float(segment.find("./*[@name='lg']").attrib['val'])
    except:
      arc=segment.find("./*[@name='arc']").attrib['val']
      radius=segment.find("./*[@name='radius']").attrib['val']
      try:
        endradius=segment.find("./*[@name='end radius']").attrib['val']
      except:
        endradius=radius
      length1=float((float(radius)+float(endradius))/2*float(arc)*math.pi/180)   
    try:
      length2=float(nextseg.find("./*[@name='lg']").attrib['val'])
    except:
      arc=nextseg.find("./*[@name='arc']").attrib['val']
      radius=nextseg.find("./*[@name='radius']").attrib['val']
      try:
        endradius=nextseg.find("./*[@name='end radius']").attrib['val']
      except:
        endradius=radius
      length2=float((float(radius)+float(endradius))/2*float(arc)*math.pi/180)
    try:
      z0=float(segment.find("./*[@name='z start']").attrib['val'])
    except:
      try:
        z0=float(tracksegs[i-1].find("./*[@name='z end']").attrib['val'])
      except:
        z0=(float(tracksegs[i-1].find("./*[@name='z end left']").attrib['val'])+float(tracksegs[i-1].find("./*[@name='z end right']").attrib['val']))/2
    try:
      z1=float(segment.find("./*[@name='z end']").attrib['val'])
    except:
      z1=(float(segment.find("./*[@name='z end left']").attrib['val'])+float(segment.find("./*[@name='z end right']").attrib['val']))/2
    try:
      z2=float(nextseg.find("./*[@name='z end']").attrib['val'])
    except:
      z2=(float(nextseg.find("./*[@name='z end left']").attrib['val'])+float(nextseg.find("./*[@name='z end right']").attrib['val']))/2
    l1=float(length1)
    l2=float(length2)
   
    endtangent=((z2-z1)*l1*l1-(z0-z1)*l2*l2)/(l1*l2*(l1+l2))*100
   
   
    try:
      segment.find("./*[@name='profil end tangent']").attrib['val'] = str(endtangent)
    except:
      #create attribute
      etree.SubElement(segment, "attnum", name="profil end tangent", unit="%", val=str(endtangent))
    i+=1   



#for att in segment:       
#        if att.attrib['name']=="z end":
#            try:
#                lastzend=float(att.attrib['val'])
#            except:
#                print "no zend defined"
#       
#       
## find position nondimensional -1 to 1
#i=firstseg
#while i<lastseg:   
#    x[i]=lgx[i]/lgges*2-1
#    i+=1
##
#i=firstseg
#while i<lastseg:   
#    i+=1
#
print 'writing file..'
fullpath = path1 + '/' + name + '.xml'
xmlfile.write(fullpath)
print 'finished'
exit()     


hope you enjoy...
simon-2
 
Posts: 89
Joined: Thu Jan 29, 2015 3:01 pm


Return to SD Tracks

Who is online

Users browsing this forum: No registered users and 1 guest

cron