Jamie Bullock wrote:
Attached is a new version with the proper code indentation (no tabs),

Thanks, cool stuff!

and some modifications to make the lines end with perpendicular edges.
This raises a general problem with this method, which is that the lines
that are filled between always maintain the same vertical distance, i.e.
the 'gap' is created by a simple y co-ordinate offset for each point.

That's really not going to cut it in general.

 To
make the lines properly parallel would require significantly more
overhead, unless there's some nice numpy function to shift a set of
co-ordinates around in the cartesian plane.

no, not really, but numpy could help. What you're doing is called "buffering" in the GIS world. A little googling may find you an algorithm. I wonder if there is anything in GraphicsContext for this. You might find something in the computational geometry literature too.

Anyway, aside from getting fancy, you may be able to "vectorize" your Perpendicular method, to create a perpendicular set along the whole line. It could be fast enough.

Depending on whether you want the line thickness to change as you zoom, you could cache the resulting points, so they only have to be calculated once.

You might be able to speed up that algorithm by computing a ratation transform matrix, rather than using the trig. Look at the code in the Arrow DrawObject for inspiration.

You were also missing a key bit: In your Draw method, you need to transform the points into pixel coords before drawing with them.

It's looking pretty nice though.

I'm so psyced that we can incorporate GraphicsContext without making ANY changes to FloatCanvas at all!

Enclosed is a slightly edited version. I"ve also got it in SVN, under Tests.

-Chris

        # Don't know what this does (HTdc?)
        # That's the HIt Test DC -- it draws a version on an
        # off screen bitmap in a unique color for hit testing

#!/usr/bin/env python

"""

An attempt at an AlphaLine class using NavCanvas and GraphicsContext

"""

import numpy as N
import sys, wx
sys.path.append("..")
from floatcanvas import FloatCanvas, NavCanvas
#from math import sin,cos,atan,pi # these are all in numpy!

class AlphaLine(FloatCanvas.Line):
    """

    The AlphaLine class takes a list of N - 2-tuples, or a NX2 NumPy Float array
    of point coordinates.

    It will draw a line.

    """
    def __init__(self,Points,
            LineColor = "Black",
            LineStyle = "Solid",
            LineWidth    = 1,
            InForeground = True,
            StartAlpha = 0,
            EndAlpha = 255,
            BorderColour = "White"):
        FloatCanvas.DrawObject.__init__(self, InForeground)

        self.Points = N.array(Points, N.float).reshape((-1,2))
        self.CalcBoundingBox()

        self.LineColor = LineColor
        self.LineStyle = LineStyle
        self.LineWidth = LineWidth
        self.StartAlpha = StartAlpha
        self.EndAlpha = EndAlpha
        self.BorderColour = BorderColour

    def Perpendicular(self, line, length):
        """Return a point that is perpendicular to 'line'

        Given a line defined as two points [(x1, y1), (x2, y2)] return a point
        that will create a new line that is perpendicular. 'length' gives the 
        vertical distance from (x2, y2)

        """
        # this could be numpy-ified
        x1,y1 = line[0]
        x2,y2 = line[1]
        angle = N.pi

        theta = N.arctan((x2 - x1) / (y2 - y1))
        alpha = 2 * N.pi - (angle + theta)
        l = length * N.cos(alpha)
        dx = l * N.sin(alpha)
        dy = l * N.cos(alpha)

        if dx != abs(dx):
            dx *= -1

        if dy != abs(dy):
            dy *= -1

        x3 = x2 - dx
        y3 = y2 - dy

        return x3, y3

    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
        bcolour = self.BorderColour

        # you need to translform to pixel coords!
        Points = WorldToPixel(self.Points)
        #Points = self.Points
        GC = wx.GraphicsContext.Create(dc)

        ##FIXME: I sure hope there is a better way to create a color!
        c = wx.Color()
        c.SetFromName(self.LineColor)
	#c = wx.Color(self.LineColor)
        r,g,b = c.Get()

        c1 = wx.Color(r, g, b, self.StartAlpha)
        c2 = wx.Color(r, g, b, self.EndAlpha)

        Path = GC.CreatePath()

        bottomline = Points[1:].copy()

        lastline = Points[-2:]
        firstline = Points[:2]
        firstline = firstline[::-1]
        perplast = self.Perpendicular(lastline, self.LineWidth)
        perpfirst = self.Perpendicular(firstline, self.LineWidth)

        data = Points[:]
        data[:,1] -= self.LineWidth

        while perplast[0] >  data[-1,0]:
            data = data[:-1]

        data = N.resize(data, (len(data) + 1, 2))
	data[-1] = perplast

        data = data[::-1]

        while  perpfirst[0] > data[-1,0]:
            data = data[:-1]

        data = N.resize(data, (len(data) + 1,2))
        data[-1] = perpfirst
        topline = data

        Path.MoveToPoint(perpfirst)

        if bottomline[-1, 0] >= perplast[0]:
            bottomline[-1] = perplast

        for point in bottomline:
            Path.AddLineToPoint(point)

        for point in topline:
            Path.AddLineToPoint(point)

        GC.SetPen(wx.Pen(bcolour))
        GC.DrawPath(Path)

        m = Points[:,0].size - 1

        Brush = \
            GC.CreateLinearGradientBrush(Points[0,0], \
            Points[0,1], Points[m,0], Points[m,1], c1, c2)

        GC.SetBrush(Brush)
        GC.FillPath(Path)

        # Don't know what this does (HTdc?)
	# That's the HIt Test DC -- it draws a version on an
	# off screen bitmap in a unique color for hit testing
        if HTdc and self.HitAble:
            HTdc.SetPen(self.HitPen)
            HTdc.DrawLines(Points)


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        kwargs['size'] = (700, 300)
        kwargs['pos'] = (50,50)
        wx.Frame.__init__(self, *args, **kwargs)

        NC = NavCanvas.NavCanvas(self ,wx.ID_ANY ,(500,500),
                ProjectionFun = None,
                BackgroundColor = "WHITE"
                )

        self.Canvas = NC.Canvas
        self.DrawLine()
        self.DrawCurve()

        return None

    def DrawLine(self):
        data = ([400,100], [100,50])

        self.Canvas.AddObject(AlphaLine(data,
                LineColor = "Red",
                LineStyle = "Solid",
                LineWidth    = 4,
                InForeground = 1,
                StartAlpha = 255,
                EndAlpha = 0,
                BorderColour = "Black"))

        self.Canvas.Draw()

    def DrawCurve(self):
        time = 2.0*N.pi*N.arange(100)/100.0
        data = 1.0*N.ones((100,2))
        data[:,0] = time * 100 + 20
        data[:,1] = N.sin(time) * 100 + 150

        self.Canvas.AddObject(AlphaLine(data,
                    LineColor = "Blue",
                    LineStyle = "Solid",
                    LineWidth    = 6,
                    InForeground = 1,
                    StartAlpha = 0,
                    EndAlpha = 255,
                    BorderColour = "White"))

        self.Canvas.Draw()

A = wx.App(0)
F = MyFrame(None)
F.Show()
A.MainLoop()
_______________________________________________
FloatCanvas mailing list
[email protected]
http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas

Reply via email to