$Id$ class ControlPoint: """ abstract class for any kind of control point for use inside another object like a polyline; subclasses can include ones on a reference object # note: obs name: PointOnReferenceObject """ pass class PolylineSegment: """ abstract class for any kind of polyline segment """ pass class ModelObject: pass # rename Model3D_Component or so? class Polyline(ModelObject): """ A version of a polyline model object type based on a graph, with control points able to be on different ref objects; this class provides the data type and necessary low-level operations; it does NOT provide drawing and editing-UI code -- when those are needed they must be provided by associated classes. """ # each one has a graph of control points and segments # which have to have types that meet some APIs for them # (which depend only on our own code, since it uses those APIs) # (but note that if methods are added to this class that might add to the list of required APIs for the parts!) controlPointType = ControlPoint ### REVIEW: capitalize attr name? # note: this is the data type, not the UI, so we have the # most general type we can; a typical UI would encourage or require # the creation of points of some more specific type; # if so, that's declared in that UI code. segmentType = PolylineSegment _graph = Instance( Graph( vertexType = controlPointType, edgeType = segmentType ) ) # Q. is Instance required? # Note: this declaration implicitly says that this member is part of our data # (to be inspected, saved, copied, etc)... # Q. but what about the private name? does it indicate we'd rather save it # in terms of the separate accessors with public names?? controlPoints = _graph.vertices segments = _graph.edges ## addControlPoint = _graph.addVertex #e or is it better to require use of controlPoints as a dict, adding or extending? # not necessarily... e.g. this method would require that the point was not already there. # OTOH we can use it as a set instead and make sure the set.add method requires that... ###k # the price of not doing this is having to alias tons of method names... # but these aliases seem useful addControlPoint = controlPoints.add addSegment = segments.add pass class Polyline_drawer(Drawer): """ a view of a polyline for the purpose of drawing it """ delegate = Arg(Polyline, doc = "the Polyline we draw") ###e rename to self.polyline? but we do need to delegate to it for convenience. def draw(self): ### IMPLEM self.drawer as something to draw objects in their usual way, # subject to usual env rules for that (drawing styles and filters); # drawset can have optims for drawing a large set; # maybe we'll pass options about their role in us, # for it to use when looking up display styles. # # OR it can just be self, if this doesn't cause confusion due to delegation # by hiding parts of self.delegate. Hmm... should we declare what attrs to delegate? self.drawer.drawset( self.controlPoints ) self.drawer.drawset( self.segments ) return pass def Polyline_UI_helper: #k super? should provide helper methods for this kind of component # (one which adds data editing ops for use by a graphical UI) """ methods for editing polylines for use by a graphical UI """ data = Arg(Polyline, doc = "the Polyline we help to edit") #e rename to self.polyline? def addControlPoint(self, pointType): #k not sure if we need this, but it's like addSegment below... # note that it takes the type, not the object! because we know it's new... # but it's not really a type, it's a description, since it has all the data as well. ### RENAME the arg! ## point = pointType() ###k ?? no, the container needs to make it, from the description... or is it an expr with code? HMM ##### point = self.data.makeControlPoint(pointType) # note: this implem is generic for things that can instantiate descriptions, # except for knowing to call makeControlPoint rather than make... # which differs from make in having defaults suitable for self.data.ControlPointType self.data.addControlPoint(point) def addSegment(self, fromPoint, toPoint, segmentType): # note: this is sort of like self.make(segmentType(fromPoint, toPoint)) # if we imagine that those descriptions (in the arg) carried enough info # for us to know their roles in self. """ Add a new segment of type Line from fromPoint to point toPoint, each of which might be either an existing Point object in self or a description of a new one we should create (e.g. as coords and ref object or frame). """ ## fromPoint = self.canonicalizePoint(fromPoint) #e rename: find or make? this adds the points if needed. # Note re choosing the name: # it is a purely UI method -- the only reason to not know, here, if it's an existing or new point, # is that we had user mouse position data, then we saw if user was hovering over an existing point, # and if so passed that point, else the mouse position. # (So should the caller turn the posn into a new point, for clarity? # maybe only the caller knows the point type, whether that's ok given what's near, etc? # YES, make caller do that, thus we comment this out.) ## toPoint = self.canonicalizePoint(toPoint) line = segmentType(fromPoint, toPoint) ###k? old code said self.Line... self.Line is a type and constructor for whatever we use as a line... # but does this method care whether the points are in the same ref object? no, it does not require that. self.data.addSegment(line) # the more fundamental method; so self.data is our polyline data, not self! ###k return def # now bring in code from: # - my polyline drawing eg code -- it can add lines or sketched curves # for the latter, add the angle cond from squeak -- # or is that part of separate UI class for a stroke? yes, it is... in this code # just say: collect a stroke from the drag, give me the points to be drawing # (if we want to optim, give me the permanent and tentative ones seply, and give permanent ones incrly) # (so my stroke-drawing code can optim for a stroke being built up, by making a displist tree...) # - my scratch code for drawing this with its construction lines, in xor mode # - if i like this way of splitting up classes, then split the file and make separate files # conclusions: # - yes, do it like this, keep this (at least as scratch to make clean & real) # - want separate classes # - want formulae # - want types # - maybe it's more like a sketch than a line... as you added sketch elements, they might share one set of control points, # and those might be kept by the sketch in per-ref-object sets for efficiency. # so one sketch element (entity) might then refer to or own various sketch primitives including points and segments. # So this object here is really more like the sketch itself. # (But do sketches share points between them? No -- if you want that, promote those points to ReferencePoints outside the sketch.) # - note we'll have folding paths, etc, in future, related to this code # - note they're like sketches in being able to have disconected pieces, other things in them, etc # - and fancier -- tags on the elements, etc # - they'd make good use of using generic helper objects but with more specific implem and UI classes for those obj's components. # (eg a graph but of a specific kind of points)