ellipse.py :  » GUI » Sketch » skencil-0.6.17 » Sketch » Graphics » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » GUI » Sketch 
Sketch » skencil 0.6.17 » Sketch » Graphics » ellipse.py
# Sketch - A Python-based interactive drawing program
# Copyright (C) 1997, 1998, 1999, 2000, 2001 by Bernhard Herzog
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from math import sin,cos,atan2,hypot,pi,fmod,floor

from Sketch.const import ArcArc,ArcChord,ArcPieSlice,ConstraintMask,\
     AlternateMask

from Sketch import _,Point,Polar,Trafo,SingularMatrix,Rect,UnionRects,\
     CreateMultiUndo, NullUndo, RegisterCommands

import graphics

from Sketch.UI.command import AddCmd
import Sketch.UI.skpixmaps
pixmaps = Sketch.UI.skpixmaps.PixmapTk

import handle
from base import Primitive,RectangularPrimitive,RectangularCreator,Creator,\
     Editor
from bezier import PolyBezier
from blend import Blend
from properties import DefaultGraphicsProperties

from Sketch import _sketch



#
#  Ellipse
#

# helper function for snapping (might be useful elsewhere too):
def snap_to_line(start, end, p):
    if start != end:
  result = [(abs(start - p), start), (abs(end - p), end)]
  v = end - start
  length = abs(v)
  r = (v * (p - start)) / (length ** 2)
  if 0 <= r <= 1.0:
      p2 = start + r * v
      result.append((abs(p2 - p), p2))
  return min(result)
    else:
  return (abs(start - p), start)



class Ellipse(RectangularPrimitive):

    is_Ellipse = 1
    is_curve = 1
    is_clip = 1
    has_edit_mode = 1

    commands = RectangularPrimitive.commands[:]

    def __init__(self, trafo = None, start_angle = 0.0, end_angle = 0.0,
     arc_type = ArcPieSlice, properties = None, duplicate = None):
  if duplicate is not None:
      self.start_angle = duplicate.start_angle
      self.end_angle = duplicate.end_angle
      self.arc_type = duplicate.arc_type
  else:
      self.start_angle = start_angle
      self.end_angle = end_angle
      self.arc_type = arc_type
  RectangularPrimitive.__init__(self, trafo, properties = properties,
              duplicate = duplicate)
  self.normalize()

    def DrawShape(self, device, rect = None, clip = 0):
  Primitive.DrawShape(self, device)
  device.SimpleEllipse(self.trafo, self.start_angle, self.end_angle,
           self.arc_type, rect, clip)

    def SetAngles(self, start_angle, end_angle):
  undo = (self.SetAngles, self.start_angle, self.end_angle)
  self.start_angle = start_angle
  self.end_angle = end_angle
  self.normalize()
  self._changed()
  return undo

    def Angles(self):
        return self.start_angle, self.end_angle

    def SetArcType(self, arc_type):
  if arc_type == self.arc_type:
      return NullUndo
  undo = (self.SetArcType, self.arc_type)
  self.arc_type = arc_type
  self._changed()
  return undo

    def ArcType(self):
        return self.arc_type

    AddCmd(commands, 'EllipseArc', _("Arc"), SetArcType, args = ArcArc)
    AddCmd(commands, 'EllipseChord', _("Chord"), SetArcType, args = ArcChord)
    AddCmd(commands, 'EllipsePieSlice', _("Pie Slice"), SetArcType,
     args = ArcPieSlice)

    def normalize(self):
  pi2 = 2 * pi
  self.start_angle = fmod(self.start_angle, pi2)
  if self.start_angle < 0:
      self.start_angle = self.start_angle + pi2
  self.end_angle = fmod(self.end_angle, pi2)
  if self.end_angle < 0:
      self.end_angle = self.end_angle + pi2

    def Paths(self):
        path = _sketch.approx_arc(self.start_angle, self.end_angle,
                                  self.arc_type)
  path.Transform(self.trafo)
        return (path,)

    def AsBezier(self):
        return PolyBezier(paths = self.Paths(),
                          properties = self.properties.Duplicate())

    def Hit(self, p, rect, device, clip = 0):
  return device.SimpleEllipseHit(p, self.trafo, self.start_angle,
               self.end_angle, self.arc_type,
               self.properties, self.Filled() or clip,
               ignore_outline_mode = clip)

    def Blend(self, other, p, q):
  blended = RectangularPrimitive.Blend(self, other, p, q)
  if self.start_angle != self.end_angle \
     or other.start_angle != other.end_angle:
      blended.start_angle = p * self.start_angle + q * other.start_angle
      blended.end_angle = p * self.end_angle + q * other.end_angle
      if self.start_angle == self.end_angle:
    blended.arc_type = other.arc_type
      elif other.start_angle == other.end_angle:
    blended.arc_type = self.arc_type
      else:
    if self.arc_type == other.arc_type:
        blended.arc_type = self.arc_type
    # The rest of the arc type blends is quite arbitrary
    # XXX: are these rules acceptable? Maybe we should blend
    # the ellipses as bezier curves if the arc types differ
    elif self.arc_type == ArcArc or other.arc_type == ArcArc:
        blended.arc_type = ArcArc
    elif self.arc_type == ArcChord or other.arc_type == ArcChord:
        blended.arc_type = ArcChord
    else:
        blended.arc_type = ArcPieSlice
  return blended

    def GetSnapPoints(self):
  t = self.trafo
  start_angle = self.start_angle; end_angle = self.end_angle
  if self.start_angle == self.end_angle:
      a = Point(t.m11, t.m21)
      b = Point(t.m12, t.m22)
      c = t.offset()
      return [c, c + a, c - a, c + b, c - b]
  else:
      points = [t(Polar(start_angle)), t(Polar(end_angle)), t.offset()]
      if end_angle < start_angle:
    end_angle = end_angle + 2 * pi
      pi2 = pi / 2
      angle = pi2 * (floor(start_angle / pi2) + 1)
      while angle < end_angle:
    points.append(t(Polar(1, angle)))
    angle = angle + pi2
      return points

    def Snap(self, p):
  try:
      r, phi = self.trafo.inverse()(p).polar()
      start_angle = self.start_angle; end_angle = self.end_angle
      p2 = self.trafo(Polar(1, phi))
      if start_angle == end_angle:
    result = (abs(p - p2), p2)
      else:
    result = []
    if phi < 0:
        phi = phi + 2 * pi
    if start_angle < end_angle:
        between = start_angle <= phi <= end_angle
    else:
        between = start_angle <= phi or phi <= end_angle
    if between:
        result.append((abs(p - p2), p2))
    start = self.trafo(Polar(self.start_angle))
    end = self.trafo(Polar(self.end_angle))
    if self.arc_type == ArcArc:
        result.append((abs(start - p), start))
        result.append((abs(end - p), end))
    elif self.arc_type == ArcChord:
        result.append((snap_to_line(start, end, p)))
    elif self.arc_type == ArcPieSlice:
        center = self.trafo.offset()
        result.append(snap_to_line(start, center, p))
        result.append(snap_to_line(end, center, p))
    result = min(result)
      return result
  except SingularMatrix:
      # XXX this case could be handled better.
      return (1e200, p)


    def update_rects(self):
  trafo = self.trafo
  start = trafo.offset()
        # On some systems, atan2 can raise a ValueError if both
        # parameters are 0. In that case, the actual value the of angle
        # is not important since in the computation of p below, the
        # coordinate depending on the angle will always be 0 because
        # both trafo coefficients are 0. So set the angle to 0 in case
        # of an exception.
        try:
            phi1 = atan2(trafo.m12, trafo.m11)
        except ValueError:
            phi1 = 0
        try:
            phi2 = atan2(trafo.m22, trafo.m21)
        except ValueError:
            phi2 = 0
  p = Point(trafo.m11 * cos(phi1) + trafo.m12 * sin(phi1),
      trafo.m21 * cos(phi2) + trafo.m22 * sin(phi2))
  self.coord_rect = r = Rect(start + p, start - p)
  if self.properties.HasLine():
      width = self.properties.line_width
      r = r.grown(width / 2 + 1)
      # add the bounding boxes of arrows
      if self.arc_type == ArcArc:
    pi2 = pi / 2
    arrow1 = self.properties.line_arrow1
    if arrow1 is not None:
        pos = trafo(Polar(1, self.start_angle))
        dir = trafo.DTransform(Polar(1, self.start_angle - pi2))
        r = UnionRects(r, arrow1.BoundingRect(pos, dir, width))
    arrow2 = self.properties.line_arrow2
    if arrow2 is not None:
        pos = trafo(Polar(1, self.end_angle))
        dir = trafo.DTransform(Polar(1, self.end_angle + pi2))
        r = UnionRects(r, arrow2.BoundingRect(pos, dir, width))
  self.bounding_rect = r

    def Info(self):
  trafo = self.trafo
  w = hypot(trafo.m11, trafo.m21)
  h = hypot(trafo.m12, trafo.m22)
  dict = {'center': trafo.offset(), 'radius': w, 'axes': (w, h)}
  if w == h:
      text = _("Circle radius %(radius)[length], "
                     "center %(center)[position]")
        else:
            text = _("Ellipse axes %(axes)[size], center %(center)[position]")
        return text, dict

    def SaveToFile(self, file):
  Primitive.SaveToFile(self, file)
  file.Ellipse(self.trafo, self.start_angle, self.end_angle,
         self.arc_type)

    def Editor(self):
  return EllipseEditor(self)

    context_commands = ('EllipseArc', 'EllipseChord', 'EllipsePieSlice')

RegisterCommands(Ellipse)


class EllipseCreator(RectangularCreator):

    creation_text = _("Create Ellipse")

    def compute_trafo(self, state):
        start = self.drag_start
        end = self.drag_cur
        if state & AlternateMask:
            # start is the center of the ellipse
            if state & ConstraintMask:
                # end is a point of the periphery of a *circle* centered
                # at start
                radius = abs(start - end)
                self.trafo = Trafo(radius, 0, 0, radius, start.x, start.y)
            else:
                # end is a corner of the bounding box
                d = end - start
                self.trafo = Trafo(d.x, 0, 0, d.y, start.x, start.y)
        else:
            # the ellipse is inscribed into the rectangle with start and
            # end as opposite corners. 
            end = self.apply_constraint(self.drag_cur, state)
            d = (end - start) / 2
            self.trafo = Trafo(d.x, 0, 0, d.y, start.x + d.x, start.y + d.y)
            
    def MouseMove(self, p, state):
        # Bypass RectangularCreator
        Creator.MouseMove(self, p, state)
        self.compute_trafo(state)

    def ButtonUp(self, p, button, state):
        Creator.DragStop(self, p)
        self.compute_trafo(state)

    def DrawDragged(self, device, partially):
  device.DrawEllipse(self.trafo(-1, -1), self.trafo(1, 1))

    def CurrentInfoText(self):
        t = self.trafo
        data = {}
        if abs(round(t.m11, 2)) == abs(round(t.m22, 2)):
            text = _("Circle %(radius)[length], center %(center)[position]")
            data['radius'] = t.m11
        else:
            text = _("Ellipse %(size)[size], center %(center)[position]")
            data['size'] = (abs(t.m11), abs(t.m22))
        data['center'] = t.offset()
        return text, data

    def CreatedObject(self):
  return Ellipse(self.trafo,
                       properties = DefaultGraphicsProperties())


class EllipseEditor(Editor):

    EditedClass = Ellipse

    selection = 0

    def ButtonDown(self, p, button, state):
  if self.selection == 1:
      start = self.trafo(cos(self.start_angle), sin(self.start_angle))
  else:
      start = self.trafo(cos(self.end_angle), sin(self.end_angle))
  Editor.DragStart(self, start)
  return p - start

    def apply_constraint(self, p, state):
  if state & ConstraintMask:
      try:
    inverse = self.trafo.inverse()
    p2 = inverse(p)
    r, phi = p2.polar()
    pi12 = pi / 12
    angle = pi12 * floor(phi / pi12 + 0.5)
    pi2 = 2 * pi
    d1 = fmod(abs(phi - angle), pi2)
    if self.selection == 1:
        selected_angle = self.end_angle
    else:
        selected_angle = self.start_angle
    d2 = fmod(abs(phi - selected_angle), pi2)
    if d2 < d1:
        phi = selected_angle
    else:
        phi = angle
    p = self.trafo(Polar(r, phi))
      except SingularMatrix:
    pass
  return p

    def MouseMove(self, p, state):
  p = self.apply_constraint(p, state)
  Editor.MouseMove(self, p, state)

    def ButtonUp(self, p, button, state):
  p = self.apply_constraint(p, state)
  Editor.DragStop(self, p)
  start_angle, end_angle, arc_type = self.angles()
  return CreateMultiUndo(self.object.SetAngles(start_angle, end_angle),
             self.object.SetArcType(arc_type))

    def angles(self):
  start_angle = self.start_angle; end_angle = self.end_angle
  if self.arc_type == ArcChord:
      arc_type = ArcChord
  else:
      arc_type = ArcPieSlice

  try:
      inverse = self.trafo.inverse()
      p = inverse(self.drag_cur)
      if self.selection == 1:
    start_angle = atan2(p.y, p.x)
      elif self.selection == 2:
    end_angle = atan2(p.y, p.x)
      if abs(p) > 1:
    arc_type = ArcArc
  except SingularMatrix:
      pass
  if fmod(abs(start_angle - end_angle), 2 * pi) < 0.0001:
      if self.selection == 1:
    start_angle = end_angle
      else:
    end_angle = start_angle
  return (start_angle, end_angle, arc_type)

    def DrawDragged(self, device, partially):
  start_angle, end_angle, arc_type = self.angles()
  device.SimpleEllipse(self.trafo, start_angle, end_angle, arc_type)

    def GetHandles(self):
  trafo = self.trafo
  start_angle = self.start_angle; end_angle = self.end_angle
  p1 = trafo(cos(self.start_angle), sin(self.start_angle))
  if start_angle == end_angle:
      return [handle.MakeNodeHandle(p1)]
  p2 = trafo(cos(self.end_angle), sin(self.end_angle))
  return [handle.MakeNodeHandle(p1), handle.MakeNodeHandle(p2)]

    def SelectHandle(self, handle, mode):
  self.selection = handle.index + 1

    def SelectPoint(self, p, rect, device, mode):
  return 0
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.