
begintemplate DomainParmInfo
public subset, name, is_subset, save_data, pl, styleset, update, prmemb1, prmemb2
public dstyle_, proxstyle_, distalstyle_, display, setparm, selparm, flip
public using, is_using, using_, prox, dist, bild, selih, parm, export
public loop, x, p, p0, p1, showdomain, origin_, theta_, xmlwrite, ox_, oy_
objref subset, this, prox, dist, bild, tobj, tobj1, tobj2
objref g, using_, nil, selih, dk2
strdef tstr, tstr1, tstr2, tstr3, tstr4
strdef ds1, ds2, ds3, ds4

proc init() {
	parm=.5
	relparm=parm
	subset = $o1
	bild = $o2
	using_ = new List()
	g = bild.topol.g
	dstyle_ = 0 proxstyle_ = 0 distalstyle_ = 0
	prox = new Vector()
	dist = new Vector()
	ox_ = oy_ = origin_ = 0
	theta_ = 90
}

func is_subset() { return 0 }

func is_using() {local i
	for i=0, using_.count-1 {
		if (strcmp(using_.object(i).s, $s1) == 0) {
			return 1
		}
	}
	return 0
}

proc using() { local i
	for i=0, using_.count-1 {
		if (strcmp(using_.object(i).s, $s2) == 0) {
			break
		}
	}
	if ($1 == 0 && i < using_.count) {
		using_.object(i).ms = nil // avoid assertion error
		using_.remove(i)
	}else if ($1 == 1 && i >= using_.count) {
		// interpreter bug when an object holds pointer to itself
		// to see keep managing and unmanaging a range variable
		// using_.append(new InhomoParm(this, $s2))
		tobj = new InhomoParm(this, $s2)
		using_.append(tobj)
		tobj = nil
	}
}

proc name() {
	i = subset.domainparms.index(this)
	if (i == 0) {
		sprint($s1, "%s_x", subset.name_)
	}else{
		sprint($s1, "%s_x%d", subset.name_, i)
	}
}

proc styleset() {
	dstyle_ = $1
	proxstyle_ = $2
	distalstyle_ = $3
}

iterator loop() {local i
	i = 0
	forsec subset.sectionlist {
		for (x, 0) {
			p = (1 - x)*prox.x[i] + x*dist.x[i]
			iterator_statement
		}
		i += 1
	}
}

proc save_data() {local i
	sprint(tstr, "{tobj1.styleset(%d, %d, %d)}", dstyle_, proxstyle_, distalstyle_) 
	$o1.save(tstr)
	if (dstyle_ == 2) {
		sprint(tstr, "{tobj1.ox_ = %g  tobj1.oy_ = %g}", ox_, oy_)
		$o1.save(tstr)
	}
	if (dstyle_ == 3) {
		sprint(tstr, "{tobj1.origin_ = %g  tobj1.theta_ = %g}", origin_, theta_)
		$o1.save(tstr)
	}
	for i=0, using_.count-1 {
		sprint(tstr, "{tobj1.using(1, \"%s\")}", using_.object(i).s)
		$o1.save(tstr)
		sprint(tstr, "tobj1.using_.object(%d)", i)
		using_.object(i).save_data($o1, tstr)
	}
}

func min() {local d
	p0 = prox.min
	d = dist.min
	if (d < p0) { p0 = d }
	return p0
}

func max() {local d
	p1 = prox.max
	d = dist.max
	if (d > p1) { p1 = d }
	return p1
}

proc pr() { local i
	printf("min=%g  max=%g\n", p0, p1)
	for i=0, subset.list.count - 1 {
		subset.list.object(i).name(tstr)
		printf("%s\t%g\t%g\n", tstr, prox.x[i], dist.x[i])
//		printf("\ttmp2=%g\n", subset.list.object(i).tmp2)
	}
}

proc showdomain() {
	g.menu_remove("Show domain value")
	g.menu_remove("Specify origin")
	g.menu_remove("Specify projection axis")
	g.menu_tool("Show domain value", "selparm1")
	g.exec_menu("Show domain value")
	relparm=.5 setparm(relparm)
}

proc flip() {
	dk2 = $o1
	$o1.flip_to(-1)
	$o1.remove_last()
	$o1.intercept(1)
	panel()
	$o1.intercept(0)
	$o1.flip_to(1)
	relparm=.5 setparm(relparm)
}

proc panel() {
//print "panel"
	xpanel("")
	display(ds1, ds2, ds3, ds4)
	xvarlabel(ds1)
	xvarlabel(ds2)
	xvarlabel(ds3)
	xvarlabel(ds4)
	xslider(&relparm, 0, 1, "setparm(relparm)", 0, 1)
	showdomain()
	if (dstyle_ == 2) {
		g.menu_tool("Specify origin", "orgloc")
		xpvalue("x (um)", &ox_, 1, "chorg1()")
		xpvalue("y (um)", &oy_, 1, "chorg1()")
	}
	if (dstyle_ == 3) {
		g.menu_tool("Specify projection axis", "projax")
		xpvalue("origin_ (um)", &origin_, 1, "chorg()")
		xpvalue("Angle (deg)", &theta_, 1, "chorg()")
	}
	xmenu("metric")
	xbutton("Path Length from root", "dstyle(0)")
//	xbutton("Path Length from origin_", "dstyle(1)")
	xbutton("3D radial position", "dstyle(2)")
	xbutton("3D projection onto line", "dstyle(3)")
//	xbutton("3D distance from line", "dstyle(4)")
	xmenu()
	xmenu("proximal", "proxmenu()", 1)
	xmenu("distal", "distmenu()", 1)
	xlabel("")
	xbutton("Remove", "bild.subsets.delsdi()")
	xpanel()
}

proc proxmenu() {
	xbutton("No translation", "proxstyle(0)")
	if (dstyle_ == 0) {
		xbutton("Most proximal at 0", "proxstyle(1)")
		xbutton("All proximal ends at 0", "proxstyle(2)")
	}else if (dstyle_ == 2) {
		xbutton("Closest point to origin is 0", "proxstyle(1)")
	}else if (dstyle_ == 3) {
		xbutton("Minimum projection value is 0", "proxstyle(1)")
	}
}
proc distmenu() {
	xbutton("No normalization", "distalstyle(0)")
	if (dstyle_ == 0) {
		xbutton("Most distal at 1", "distalstyle(1)")
		xbutton("All distal ends at 1", "distalstyle(2)")
	}else if (dstyle_ == 2) {
		xbutton("Furthest point from origin is 1", "distalstyle(1)")
	}else if (dstyle_ == 3) {
		xbutton("Maximum projection value is 1", "distalstyle(1)")
	}
}

proc selparm1() {local x, dx, dy  localobj tobj
	bild.topol.nearest_sec($2,$3,tobj)
	if (tobj.selected == 1) {
		dx = tobj.x1 - tobj.x0
		dy = tobj.y1 - tobj.y0
		x = (($2 - tobj.x0)*dx + ($3 - tobj.y0)*dy)/(dx^2 + dy^2)
		if (x > 1) { x = 1 } else if (x < 0) { x = 0 }
		relparm = selparm(x, tobj)
	}else{
		setparm(0)
	}
}

proc projax() {
	if ($1 == 2) { // mouse down, what is selected
		if (ptdist($2,$3, ox_, oy_) < 100) {
			adjust_ = 1
		}else{
			adjust_ = 2
		}
	}
	if (adjust_ == 1) {
		ox_ = $2
		oy_ = $3
	}
	if (adjust_ == 2) {
		theta_ = atan2($3 - oy_, $2 - ox_)*DEG
	}
	origin_ = ox_*cos(theta_/DEG) + oy_*sin(theta_/DEG)
	chorg1()
}

proc orgloc() {
	ox_ = $2
	oy_ = $3
	chorg1()
}

func ptdist() {local i, x1, y1, x2, y2
	i = g.view_info()
	x1 = g.view_info(i, 13, $1)
	y1 = g.view_info(i, 14, $2)
	x2 = g.view_info(i, 13, $3)
	y2 = g.view_info(i, 14, $4)
	return (x1 - x2)^2 + (y1 - y2)^2
}

proc dstyle() {
	dstyle_ = $1
	flip(dk2)
	display(ds1, ds2, ds3, ds4)
}
proc proxstyle() {
	proxstyle_ = $1
	display(ds1, ds2, ds3, ds4)
}
proc distalstyle() {
	distalstyle_ = $1
	display(ds1, ds2, ds3, ds4)
}

proc display() {
	need_update_ = 1
	export()
//	pr()

	if (proxstyle_ == 0) { $s2 = "with no translation" }
	if (distalstyle_ == 0) { $s3 = "and no normalization" }
	if (dstyle_ == 0) {
		$s1 = "Path Length from root"
if (proxstyle_ == 1) { $s2 = "translated so most proximal end at 0" }
if (proxstyle_ == 2) { $s2 = "translated so all proximal ends shifted to 0" }
if (distalstyle_ == 1) { $s3 = "and normalized so most distal end at 1" }
if (distalstyle_ == 2) { $s3 = "and normalized so all distal ends at 1" }
	}else if (dstyle_ == 1) { // not implemented
		$s1 = "Path Length from origin"
	}else if (dstyle_ == 2) {
		$s1 = "3D radial position from specified location"
if (proxstyle_ == 1) { $s2 = "with closest point at 0" }
if (distalstyle_ == 1) { $s3 = "and normalized so furthest point at 1" }
	}else if (dstyle_ == 3) {
		$s1 = "3D projection onto line"
if (proxstyle_ == 1) { $s2 = "with minimum projection value at 0" }
if (distalstyle_ == 1) { $s3 = "and normalized so max is 1" }
	}else if (dstyle_ == 4) { // not implemented
		$s1 = "3D distance from line"
	}

	sprint($s4, "ranges from %g to %g", p0, p1)

	setparm(relparm)
}

proc update() {
	if (geom_cnt_ != bild.geom_cnt_) {
		need_update_ = 1
		geom_cnt_ = bild.geom_cnt_
	}
	if (!need_update_) { return }
//	printf("%s.update\n", this)
	if (subset.list.count != prox.size) {
		prox.resize(subset.list.count)
		dist.resize(subset.list.count)
	}
	if (dstyle_ == 0) {
		update_path()
	}else if (dstyle_ == 2) {
		update_sphere()
	}else if (dstyle_ == 3) {
		update_proj()
	}
	min()
	max()
	sprint(ds4, "ranges from %g to %g", p0, p1)
	need_update_ = 0
}

proc update_path() { local x
	absolute_path() // also does proxstyle_ == 2
	if (proxstyle_ == 1) { // shift by most prox value
		x = min()
		prox.sub(x)
		dist.sub(x)
	}
	if (distalstyle_ == 1) { // normalize by most distal value
		x = max()
		prox.div(x)
		dist.div(x)
	}else if (distalstyle_ == 2) { // normalize so all distal values = 1
		alldist1()
	}
}

proc update_sphere() {local x
	spherical_loc()
	if (proxstyle_ == 1) { // shift by least value
		x = min()
		prox.sub(x)
		dist.sub(x)
	}
	if (distalstyle_ == 1) { // normalize by greatest value
		x = max()
		prox.div(x)
		dist.div(x)
	}
}
proc update_proj() {local x
	absolute_proj()
	if (proxstyle_ == 1) { // shift by least value
		x = min()
		prox.sub(x)
		dist.sub(x)
	}
	if (distalstyle_ == 1) { // normalize by greatest value
		x = max()
		prox.div(x)
		dist.div(x)
	}
}

proc absolute_path() {local l
	// absolute arc lengths --- possibly shifted so all prox ends at 0
	tobj = bild.topol.slist

	// cannot use tobj1.selected since we may be exporting
	// due to change to another subset parameter
	for i=0, tobj.count-1 { tobj.object(i).tmp3 = 0 }
	for i=0, subset.list.count-1 { subset.list.object(i).tmp3 = 1 }

	for i=0, tobj.count-1 {
		tobj1 = tobj.object(i)
		if (proxstyle_ == 2 && tobj1.tmp3 == 0) {
			l = 0
		}else{
			l = len(tobj1)
		}
		if (i == 0) {
			tobj1.tmp1 = 0
		}else if (tobj1.xparent == 1) {
			tobj1.tmp1 = tobj1.parent.tmp2
		}else{
			tobj1.tmp1 = tobj1.parent.tmp1
		}
		tobj1.tmp2 = tobj1.tmp1 + l
	}
	for i=0, subset.list.count-1 {
		tobj = subset.list.object(i)
		prox.x[i] = tobj.tmp1
		dist.x[i] = tobj.tmp2
	}
}

proc alldist1() {local i, nfac
	// arc lengths normalized so all distal ends at 1
	// note that branch point arc values are normalized with
	// respect to longest distal length from branch. i.e the branch
	// point of ___|___ has a value of 3/6 instead of 1/4
	//
	// strategy is to fill tmp2 with proper normalization factors which
	// are the most distal tips from that section.
	// first fill tmp2 with current distal values
	// but need to 0 so parent will be set corrrectly
	for i = 0, bild.topol.slist.count-1 {
		bild.topol.slist.object(i).tmp2 = 0
	}
	for i = 0, subset.list.count-1 { subset.list.object(i).tmp2 = dist.x[i] }
	for (i=subset.list.count-1; i >= 0; i -= 1) { // from leaf order
		tobj = subset.list.object(i)
		if (object_id(tobj.parent) != 0) if (tobj.parent.tmp2 < tobj.tmp2) {
			tobj.parent.tmp2 = tobj.tmp2
		}
	}
	// normalize prox and dist
	for i=0, subset.list.count-1 {
		tobj = subset.list.object(i)
		dist.x[i] /= tobj.tmp2
		if (object_id(tobj.parent) != 0) {
			// if there is no parent then it is the root and so
			// 0 anyway
			prox.x[i] /= tobj.parent.tmp2
		}
	}
}

func len() {local i, a
	a = 0
	if ($o1.i3d > 0) {
		tobj2 = $o1.p3d.x.c.deriv(1,1).pow(2)
		tobj2.add($o1.p3d.y.c.deriv(1,1).pow(2))
		tobj2.add($o1.p3d.z.c.deriv(1,1).pow(2))
		a = tobj2.sqrt.sum
	}else if (object_id($o1.ldiaminfo)) {
		a = $o1.ldiaminfo.x[0]
	}else if (object_id($o1.geo)) {
		for i=0, $o1.geo.count-1 {
			if ($o1.geo.object(i).type == 2) {
				a = $o1.geo.object(i).value
				return a
			}else if ($o1.geo.object(i).type == 4) {
				a = $o1.geo.object(i).value
				a = sqrt(a/PI)
				return a
			}
		}
		$o1.name(tstr)
		print "cannot determine length of ", tstr, ", assume 500"
		a = 500
	} else { // either unspecified or via a subset constant
		$o1.name(tstr)
		print "cannot determine length of ", tstr, ", assume 500"
		a = 500
	}
	//printf("%s[%d] %g\n", $o1.name_, $o1.id, a)
	return a
}

proc absolute_proj() {local i, x, y, c, s, n
	tobj = bild.topol.slist

	c = cos(theta_/DEG)  s = sin(theta_/DEG)
	for i=0, subset.list.count-1 {
		tobj = subset.list.object(i)
		if (tobj.i3d > 0) {
			x = tobj.p3d.x.x[0]
			y = tobj.p3d.y.x[0]
			prox.x[i] = x*c + y*s
			n = tobj.p3d.x.size - 1
			x = tobj.p3d.x.x[n]
			y = tobj.p3d.y.x[n]
			dist.x[i] = x*c + y*s
		} else {
execerror("3D projection onto line only works with 3-d reconstructions")
		}
	}
	dist.sub(origin_)
	prox.sub(origin_)
}

proc spherical_loc() {local i, x, y, z
	tobj = bild.topol.slist

	for i=0, subset.list.count-1 {
		tobj = subset.list.object(i)
		if (tobj.i3d > 0) {
			x = tobj.p3d.x.x[0] - ox_
			y = tobj.p3d.y.x[0] - oy_
			z = tobj.p3d.z.x[0]
			prox.x[i] = x*x + y*y + z*z
			n = tobj.p3d.x.size - 1
			x = tobj.p3d.x.x[n] - ox_
			y = tobj.p3d.y.x[n] - oy_
			z = tobj.p3d.z.x[n]
			dist.x[i] = x*x + y*y + z*z
		} else {
execerror("3D radial position only works with 3-d reconstructions")
		}
	}
	dist.sqrt()
	prox.sqrt()
}

proc setparm() {
	parm = p0 + $1*(p1-p0)
	pl()
}

proc chorg() {local s, c, x
	// preserve the line but make ox, oy consistent with origin
	s = sin(theta_/DEG)  c = cos(theta_/DEG)
	// well, what is the line position at current ox_, oy_
	x = ox_*c + oy_*s
	// so translate along line by
	x = origin_ - x
	ox_ += x*c
	oy_ += x*s
	chorg1()
}

proc chorg1() {
	need_update_ = 1
	update()
	setparm(relparm)
}

func selparm() {local i, x
	i = subset.list.index($o2)
	if (i >= 0) {
		parm = prox.x[i] + $1*(dist.x[i] - prox.x[i])
	}else{
		parm = 0
	}
	pl()
	$o2.name(tstr)
	g.label(.7,.05, tstr, 2, 1, 1, 0, 1)
	sprint(tstr, " (%g)", $1)
	g.label(.7,.05, tstr, 2, 1, 0, 0, 1)
	if (object_id(selih)){selih.selpos(parm)}
	return (parm - p0)/(p1 - p0)	
}

proc pl() {local i, th, x, y
	g.erase_all()
	
	tobj = bild.topol.slist
	for i=0, tobj.count-1 {
		tobj1 = tobj.object(i)
		if (tobj1.selected == 0) {
			g.beginline(9,1)
			g.line(tobj1.x0, tobj1.y0)
			g.line(tobj1.x1, tobj1.y1)
		}
	}

	for i=0, subset.list.count - 1 {
		tobj = subset.list.object(i)
if (dist.x[i] < prox.x[i]) {
		if (prox.x[i] <= parm) {
			g.beginline(2,2)
			g.line(tobj.x0, tobj.y0)
			g.line(tobj.x1, tobj.y1)
		}else if (dist.x[i] >= parm) {
			g.beginline(1,2)
			g.line(tobj.x0, tobj.y0)
			g.line(tobj.x1, tobj.y1)
		}else{
			th = (parm - prox.x[i])/(dist.x[i] - prox.x[i])
			x = (1-th)*tobj.x0 + th*tobj.x1
			y = (1-th)*tobj.y0 + th*tobj.y1
			g.beginline(2,2)
			g.line(tobj.x1, tobj.y1)
			g.line(x, y)
			g.beginline(1,2)
			g.line(x, y)
			g.line(tobj.x0, tobj.y0)
		}
}else{
		if (dist.x[i] <= parm) {
			g.beginline(2,2)
			g.line(tobj.x0, tobj.y0)
			g.line(tobj.x1, tobj.y1)
		}else if (prox.x[i] >= parm) {
			g.beginline(1,2)
			g.line(tobj.x0, tobj.y0)
			g.line(tobj.x1, tobj.y1)
		}else{
			th = (parm - prox.x[i])/(dist.x[i] - prox.x[i])
			x = (1-th)*tobj.x0 + th*tobj.x1
			y = (1-th)*tobj.y0 + th*tobj.y1
			g.beginline(2,2)
			g.line(tobj.x0, tobj.y0)
			g.line(x, y)
			g.beginline(1,2)
			g.line(x, y)
			g.line(tobj.x1, tobj.y1)
}
		}
	}
	if (dstyle_ == 2) {
		g.mark(ox_, oy_, "s", 10, 3, 1)
	}
	if (dstyle_ == 3) {
		projax_pl()
	}
	sprint(tstr, "p=%g", parm)
	g.label(.7,.1,tstr,2,1,0,0,1)
	g.flush()
}

proc projax_pl() {local dx, dy, sc
	sc = (g.size(2) - g.size(1))
	dx = cos(theta_/DEG)
	dy = sin(theta_/DEG)
	g.beginline(3, 1)
	sc = g.size(3) - oy_
	g.line(sc*dx + ox_, sc*dy + oy_)
	sc = g.size(4) - oy_
	g.line(sc*dx + ox_, sc*dy + oy_)
	g.mark(ox_, oy_, "s", 10, 3, 1)
}

proc prmemb1() {local i
	display(tstr1, tstr2, tstr3, tstr4)
	$o1.printf("  // %s %s\n", tstr1, tstr2)
	$o1.printf("  //   %s %s\n", tstr3, tstr4)
	$&2 += 2
	name(tstr)
$o1.printf("  %s = new SubsetDomainIterator(%s, %d, %d, %d", tstr, subset.name_, dstyle_, proxstyle_, distalstyle_)
	if (dstyle_ == 2) {
		$o1.printf(", %g, %g, %g", ox_, oy_, 0)
	}
	if (dstyle_ == 3) {
		$o1.printf(", %g, %g", origin_, theta_)
	}
$o1.printf(")\n")
	for i=0, using_.count-1 {
		$o1.printf("  %s_%s()\n", using_.object(i).s, tstr)
		$&2 += 1
		if ($&2 > 200) {
			$&2 = 0
			$&3 += 1
$o1.printf("  biophys_inhomo_%d()\n}\nproc biophys_inhomo_%d() {\n", $&3, $&3)
		}
	}
}

proc prmemb2() {local i
	name(tstr)	
	for i=0, using_.count-1 {
		using_.object(i).prmemb($o1, tstr)
	}
}

proc export() {local i
	update()
	for i=0, using_.count-1 {
		using_.object(i).export()
	}
}

proc xmlwrite() {local i
	name(tstr)
	$o1.printf("  <inhomogeneous_subset name=\"%s\">\n", tstr)
	subset.name(tstr)
	$o1.printf("    <subset name=\"%s\"/>\n", tstr)
	$o1.printf("    <domain_parameter>\n")
	display(ds1, ds2, ds3, ds4)
	$o1.printf("      %s\n      %s\n      %s\n      %s\n", ds1, ds2, ds3, ds4)
	if (dstyle_ == 3) {
$o1.printf("      <projection_axis origin=\"%g\" angle=\"%g\"/>\n", origin_, theta_)
	}
	$o1.printf("    </domain_parameter>\n")
	for i=0, using_.count-1 {
		using_.object(i).xmlwrite($o1)
	}
	$o1.printf("  </inhomogeneous_subset>\n", tstr)
}

endtemplate DomainParmInfo
